1

Why does the following return 1?

new BigDecimal(0.82).setScale(5, BigDecimal.ROUND_HALF_DOWN)
    .compareTo(new BigDecimal(0.82))

I expect this to return 0 because BigDecimal.compareTo ignores scale according to its documentation:

Compares this BigDecimal with the specified BigDecimal. Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. [...]

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
dmwong2268
  • 3,315
  • 3
  • 19
  • 20
  • 2
    Because `new BigDecimal(0.82)` is `0.81999999999999995115018691649311222136020660400390625 `. **DO NOT** use [`new BigDecimal(double)`](https://docs.oracle.com/javase/10/docs/api/java/math/BigDecimal.html#%3Cinit%3E(double)). Use [`BigDecimal.valueOf​(double)`](https://docs.oracle.com/javase/10/docs/api/java/math/BigDecimal.html#valueOf(double)): *This is generally the preferred way to convert a `double` (or `float`) into a `BigDecimal`*. – Andreas Jun 13 '18 at 23:46

2 Answers2

7

new BigDecimal(0.82) is not actually 0.82, because you're passing a double value -- the double closest to 0.82, which is not exactly 0.82 -- to the constructor, so new BigDecimal(0.82) is a BigDecimal equal to the double closest to 0.82.

Instead, use new BigDecimal("0.82").

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • To be specific: `new BigDecimal(0.82)` is 0.81999999999999995115018691649311222136020660400390625. Just print it and see for yourself. And `new BigDecimal(0.82).setScale(5, BigDecimal.ROUND_HALF_DOWN)` is 0.82000. – Andreas Jun 13 '18 at 23:46
  • Can you elaborate on that? Explain why this affects the `compareTo` and how `setScale` plays a role here. – Zabuzard Jun 13 '18 at 23:47
  • If `double` value is not a literal, use [`BigDecimal.valueOf​(double)`](https://docs.oracle.com/javase/10/docs/api/java/math/BigDecimal.html#valueOf(double)). – Andreas Jun 13 '18 at 23:48
  • `setScale` doesn't play a role here, really, other than making the `BigDecimal` equal to `0.82`. – Louis Wasserman Jun 13 '18 at 23:48
  • @Andreas, if your double value is not a literal, you've almost certainly done something wrong already. Your value should have been a `BigDecimal` in the first place. – Louis Wasserman Jun 13 '18 at 23:48
  • @LouisWasserman Not always a choice, if using 3rd-party libraries, or legacy code. Knowing that `new BigDecimal(double)` is **bad**, but there is a good alternative in `BigDecimal.valueOf(double)`, is certainly useful information. – Andreas Jun 13 '18 at 23:49
  • @LouisWasserman - I thought setScale would round 0.81999999999999995115018691649311222136020660400390625 to 0.82000. can you explain why the compareTo does not return 0? – dmwong2268 Jun 13 '18 at 23:52
  • 3
    @Zabuza `setScale` did. And 0.82000.compareTo(0.81999999999999995115018691649311222136020660400390625) is supposed to be >0 – Andreas Jun 13 '18 at 23:52
  • You don't need to explain this to me. It was meant more as a comment to edit and improve the answer. Because currently it answers the question only indirect. – Zabuzard Jun 13 '18 at 23:53
  • @Zabuza Then say that in a comment. Don't ask like you don't know, because then you'll just get answers to the comment like I just did. – Andreas Jun 13 '18 at 23:54
2

The problem is that you use the constructor BigDecimal#BigDecimal(double). It returns a BigDecimal that represents the double's binary floating-point value (see its documentation). However, 0.82 isn't representing the value 0.82 but

0.81999999999999995115018691649311222136020660400390625

Because of that .setScale(5, BigDecimal.ROUND_HALF_DOWN) actually changes the value, not only the scale. The resulting value after applying the method is

0.82000

The compareTo method ignores the scale but not different values. As explained, your two BigDecimal actually represent different values. Thus the compareTo does not return 0:

0.82000 != 0.81999999999999995115018691649311222136020660400390625

See the documentation of the method:

Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. [...]

Zabuzard
  • 25,064
  • 8
  • 58
  • 82