Strange BigDecimal result

Hello friends,

I am a bit confused at this behavior of BigDecimal, maybe someone has insight.

When I compare:
java.math.BigDecimal(0) == java.math.BigDecimal(0.00) // true
However, when I compare:
java.math.BigDecimal(0) == (0.00).toBigDecimal() // false

Why is there a difference when converting from Double? I see that the standard library first converts double to String before making it a BigDecimal. Can this be why? And why convert it to String first??

It really depends on your definition, but BigDecimal#equals() is pretty much broken because it also checks the internal scale field. Therefore two BigDecimals can appear to not equal each other (by equals()) although they mathematically represent exactly the same number. This is why you should never use BigDecimal#equals() (unless you know exactly what you’re doing and comparing the scale is something you actually wanna do, for whatever reason) and use BigDecimal#compareTo() instead. This function will behave as you’d expect it mathematically regarding equality (just check if the result is 0, then the two values are the same). Ironically, the JavaDoc for the Comparable interface says that the compareTo() implementation should ideally always align with the equals() implementation, which is not the case for BigDecimals

Another remark here is that you should never initialize BigDecimals with Doubles as long as possible, especially in cases where the initialization is a literal. Doubles always come with an inherent inaccuracy (for example, you cannot accurately represent the decimal value 0.1 with a binary floating-point data type). Therefore if you initialize a BigDecimal with a Double literal, chances are your BigDecimal will carry inaccuracy from the very beginning (which you probably wanted to avoid by using BigDecimal). Therefore you should always initialize your BigDecimals with strings, as the BigDecimal class can correctly convert a string into a numeric value without any inaccurate floating-point representations.

4 Likes

If you look at the implementation of BigDecimal, the valueOf(double) function returns new BigDecimal(Double.toString(val)). So in that case it would return the same as creating a BigDecimal from a String. In the case of using the constructor new BigDecimal(double), what you said is true.

Trying it with jshell gives me this:

jshell> import java.math.*

jshell> BigDecimal.valueOf(0.1)
$2 ==> 0.1

jshell> new BigDecimal(0.1)
$3 ==> 0.1000000000000000055511151231257827021181583404541015625

So if you’re creating a BigDecimal from a double literal, use the valueOf function to avoid issues.

2 Likes