Java: BigDecimal's Big Problems
In Java, the BigDecimal class has a failed abstraction around equality comparisons that will inevitably lead to bugs. There are 2 problems:
1. the method compareTo is inconsistent with the method equals
Equality (equals) of two BigDecimals takes scales (number of decimal places) into account, whereas comparison (compareTo) doesn't.
So,
new BigDecimal("1.00").equals(new BigDecimal("1.0"))
returns false, and
new BigDecimal("1.00").compareTo(new BigDecimal("1.0"))
returns 0 (zero), indicating that they're equal.
I've seen some threads stating that equals is consistent with the way that an engineering (or scientific) quantity should work, where scale is very important. Since operations with numbers with different scales yield different results, it seems reasonable that equals returns false for these numbers. So, from this standpoint, the equals implementation is right, and compareTo should be consistent and return that the numbers are not equal, maybe ordered by the number of decimal places.
Which brings us to the second problem:
2. the method equals is incompatible with the real world meaning of equality
Engineering perspectives aside, the fact that 1.00 is not equal to 1.0 is really incompatible with at least my world view of equality. The ultimate argument for me is that the following statement is always a bug, and should never be seen in the wild:
BigDecimal.ZERO.equals(anotherBigDecimal)
This will only be true if anotherBigDecimal has no scale (and of course, is zero). If I didn't want a scale, I guess I'd be using BigInteger. Why provide a ZERO value if it can never be equaled reliably with other values?
A possible solution would be if the scale wasn't considered for equality by default. The comparison methods should have overloaded versions that consider it. This way, the choice to compare based on scale would be explicit, eliminating this source of bugs.
Hi,
ReplyDeleteGood discussion!
I ran into this problem as well.
I am using a BigInteger in another class and it takes part of the equals function in that class.
I solved the problem by using compareTo instead:
if (value.compareTo(other.value) != 0)
return false;
Where value is a BigInteger. It seems to do the trick!
Yes @Hoakz, using `compareTo` seems to be the way to go!
Delete