Why does new BigDecimal("0.015").compareTo(new BigDecimal(0.015))
return -1?
If I expect those two to be equal, is there an alternative way to compare them?

- 20,235
- 26
- 86
- 135
-
7Check out this post: http://stackoverflow.com/questions/6058984/bigdecimal-compareto-not-working-as-expected?rq=1 – MFazio23 Sep 28 '15 at 20:37
-
Would be better to make use of `new BigDecimal("0.015")`. For the reason that @Reimeus pointed on in his answer. – Nicholas Robinson Sep 28 '15 at 20:38
-
You could do the comparison the same way you would compare two doubles that resulted from different calculations - accept as equal anything that is close enough, for the purposes of your program. – Patricia Shanahan Sep 28 '15 at 20:47
-
because `0.015` is already not `0.015` when it is passed to BigDecimal, while `"0.015"` is parsed by `BigDecimal` itself. – njzk2 Sep 28 '15 at 22:01
4 Answers
Due to the imprecise nature of floating point arithmetic, they're not exactly equal
System.out.println(new BigDecimal(0.015));
displays
0.01499999999999999944488848768742172978818416595458984375

- 158,255
- 15
- 216
- 276
-
1Whereas `System.out.println(new BigDecimal("0.015"));` displays exactly 0.015. – gla3dr Sep 28 '15 at 20:37
-
1If I expect ```new BigDecimal(0.015)``` to be equal to ```new BigDecimal("0.015")```, how can I compare them? – Glide Sep 28 '15 at 20:38
-
3you should not expect them to be equal. whenever you use floating point arithmetic you should be aware that these kind of errors will occur. – vonPetrushev Sep 28 '15 at 20:39
-
@Glide you can always set the precision of your `BigDecimal` ie, `System.out.println(new BigDecimal(0.015).setScale(3, BigDecimal.ROUND_HALF_UP));` prints `0.015` – SnakeDoc Sep 28 '15 at 20:39
-
4@Glide: If you expect new BigDecimal(0.015) to be equal to new BigDecimal("0.015"), then you should expect to be wrong, and you should expect that you'll have to expect something different. – Louis Wasserman Sep 28 '15 at 20:44
-
3@LouisWasserman Er.....I (as a non-Java programmer and thus an outsider) was under the impression that the *entire point* of `BigDecimal` is to accurately represent *decimal numbers*. Heck, the [documentation](http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html) even says *arbitrary precision* decimal numbers! If `0.015` is not represented in a unique way, how the heck is the `BigDecimal` class supposed to be useable?? – Kyle Strand Sep 28 '15 at 21:05
-
5@KyleStrand: The point is that you have to pass _decimal numbers in_, and writing `new BigDecimal(0.015)` stores `0.015` as a `double` -- _not_ a decimal number -- before it even gets to `BigDecimal`. You have to pass it in as a `String`. – Louis Wasserman Sep 28 '15 at 21:15
-
@LouisWasserman Well, that actually makes complete sense, and I withdraw my objection. – Kyle Strand Sep 28 '15 at 21:29
-
2I guess my only complaint, then, is that `BigDecimal` even provides a constructor from `double` in the first place. – Kyle Strand Sep 28 '15 at 21:31
-
@KyleStrand I think it's logical to provide the constructor. You might need to interoperate with numbers stored in that form. However, you need to know what kind of precision you expect from the number, and you should probably call `setScale(2, BigDecimal.ROUND_HALF_UP)` with an appropriate number of decimal places before actually trying to do any math. Maybe you would be more accepting of the constructor if it was `BigDecimal(double, int, int)` where the second argument specified how many decimal places to keep and the third was a rounding mode? – jpmc26 Sep 28 '15 at 22:45
-
@jpmc26 Absolutely--I think an explicit constructor like that would be a much better API (though the third argument should be some kind of `enum` rather than `int`). Even providing the same behavior currently provided by the `double` constructor inside of an explicitly-named `static` factory-function, such as `BigDecimal::CreateFromNumericPrimitive(double)` would be better, because it would remind users that they're actually relying on the data stored in a *primitive* type (which will of course be in some kind of base-2 representation). – Kyle Strand Sep 28 '15 at 23:02
-
To expand on the answer from @Reimeus, the various constructors for BigDecimal accept different types of input. The floating point constructors, take a floating point as input, and due to the limitations of the way that floats/doubles are stored, these can only store accurately values that are a power of 2.
So, for example, 2⁻², or 0.25, can be represented exactly. 0.875 is (2⁻¹ + 2⁻² + 2⁻³), so it can also be represented accurately. So long as the number can be represented by a sum of powers, where the upper and lower power differ by no more than 53, then the number can be represented exactly. The vast majority of numbers don't fit this pattern!
In particular, 0.15 is not a power of two, nor is it the sum of a power of two, and so the representation is not accurate.
The string constructor on the other hand does store it accurately, by using a different format internally to store the number. Hence, when you compare the two, they compare as being different.

- 1
- 1

- 27,756
- 10
- 52
- 74
-
More correctly, "integer multiples of powers of two". 0.75 can be represented exactly. – Patricia Shanahan Sep 28 '15 at 20:44
-
-
A double
cannot exactly represent the value 0.015
. The closest value it can represent in its 64 binary bits is 0.01499999999999999944488848768742172978818416595458984375
. The constructor new BigDecimal(double)
is designed to preserve the precise value of the double
argument, which can never be exactly 0.015
. Hence the result of your comparison.
However, if you display that double
value, for example by:
System.out.println(0.01499999999999999944488848768742172978818416595458984375);
it outputs 0.015
– which hints at a workaround. Converting a double
to a String
chooses the shortest decimal representation needed to distinguish it from other possible double
values.
Thus, if you create a BigDecimal
from the double
's String
representation, it will have a value more as you expect. This comparison is true:
new BigDecimal(Double.toString(0.015)).equals(new BigDecimal("0.015"))
In fact, the method BigDecimal.valueOf(double)
exists for exactly this purpose, so you can shorten the above to:
BigDecimal.valueOf(0.015).equals(new BigDecimal("0.015"))
You should use the new BigDecimal(double)
constructor only if your purpose is to preserve the precise binary value of the argument. Otherwise, call BigDecimal.valueOf(double)
, whose documentation says:
This is generally the preferred way to convert a
double
(orfloat
) into aBigDecimal
.
Or, use a String
if you can and avoid the subtleties of double
entirely.

- 48,794
- 16
- 117
- 146
What actually happens here is this:
0.015
is a primitive double. Which means that as soon as you write it, it is already no longer 0.015, but rather0.0149...
. The compiler stores it as a binary representation in the bytecode.BigDecimal
is constructed to store exactly whatever is given to it. In this case,0.0149...
BigDecimal
is also able to parse Strings into exact representations. In this case"0.015"
is parsed into exactly0.015
. Even thoughdouble
cannot represent that number,BigDecimal
can- Finally, when you compare them, you can see that they are not equal. Which makes sense.
Whenever using BigDecimal
, be cautious of the previously used type. String
, int
, long
will remain exact. float
and double
have the usual precision caveat.

- 38,969
- 7
- 69
- 107