12

I've just recently come across a behavior inside BigDecimal that I wasn't previously aware of. I've always used them as an alternative to double for areas where precision is important. For example in financial calculations.

However I recently came across this fact

new BigDecimal("1.0").equals(new BigDecimal("1")) == false

I have to admit I was surprised by this. I figure it is because the first has a scale of 1 while the second has a scale of 0, but still it seems counter-intuitive. I think the reason I've never run into it before is because we've always used fixed scale BigDecimals for financial calculations.

Checking the BigDecimal documentation I can see that says that compareTo() == 0 should be used to check for equality ignoring scale while that equals() compares both the value and the scale.

Are there any other similar gotchas I should be aware of when using BigDecimals with different scales?

Tim B
  • 40,716
  • 16
  • 83
  • 128
  • 1
    You mean nothing that has not been documented in the Java docs already I believe? – Chetan Kinger Apr 23 '15 at 13:52
  • 5
    One similar gotcha is that `new BigDecimal(0.1)` and `BigDecimal.valueOf(0.1)` return different results. The first is a representation of the double value and will be something like `0.1000000000000000055511151231257827021181583404541015625` while the second is `0.1`. – Powerlord Apr 23 '15 at 13:53
  • 2
    Related (possible duplicate): http://stackoverflow.com/questions/19818537/why-is-bigdecimal-natural-ordering-inconsistent-with-equals – Maroun Apr 23 '15 at 13:54
  • I think this line of the javadoc tells it all: `The value of the number represented by the BigDecimal is therefore (unscaledValue × 10-scale).` – guido Apr 23 '15 at 13:54
  • https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#equals-java.lang.Object- so use compareTo == 0. – Joop Eggen Apr 23 '15 at 13:54
  • @bot Buried in the docs, like the behavior of equals, yes :) – Tim B Apr 23 '15 at 13:54
  • The docs for `equals` calls this out very explicitly: "Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method)." – Jon Skeet Apr 23 '15 at 13:54
  • @MarounMaroun That is not a duplicate of this question, although it is related. – Tim B Apr 23 '15 at 13:57
  • 4
    @Powerlord btw `new BigDecimal(0.1f)` != `new BigDecimal(0.1)` ;) – Peter Lawrey Apr 23 '15 at 13:57
  • 1
    Guys, please note that the OP is asking for other gotchas and he already knows the reason for the particular gotcha he has mentioned. This is not a race. – Chetan Kinger Apr 23 '15 at 13:57
  • @JonSkeet Yes, I investigated as I was writing and my title didn't reflect the final question, I've corrected that. – Tim B Apr 23 '15 at 13:57
  • See also [How to check if BigDecimal variable = 0](http://stackoverflow.com/a/10950967/256196) whose accepted answer applies to and answers this question – Bohemian Apr 23 '15 at 13:57
  • 1
    @PeterLawrey Makes sense, since floats and doubles have different precision. Also, I fixed the wording of my last comment to say double instead of float. – Powerlord Apr 23 '15 at 13:58

3 Answers3

5

There is a value and a scale of the BigDecimal. Both need to be equal for the BigDecimals to be equal. From the java docs . . .

Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).

https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#equals(java.lang.Object)

FriedSaucePots
  • 1,342
  • 10
  • 16
3

According to JavaDoc of the equals():

Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).

So equals() checks if the objects are exactly the same. compareTo() "only" compares their numeric value.

Jordi Castilla
  • 26,609
  • 8
  • 70
  • 109
3

BigDecimal equals checks that the contents of the two BigDecimal object are the same. e.g. their toString() would be the same. Just as "1.0" and "1" are not equal, nor is new BigDecimal("1.0").equals(new BigDecimal("1")) as the unscaledValue() and getScale() are both different.

The catch is that while you knew that == wouldn't compare the contents and you might have been told .equals is the solution for String, it may not do what you intended for BigDecimal.

For compareTo it has to work on what is greater than or less than and since the values are neither greater than or less than they can only be equal (but not equals).

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130