Igor's Techno Club

The Pitfalls of Comparing BigDecimals in Java

When working with floating-point numbers in Java, developers often turn to the BigDecimal class for precise calculations. However, the behavior of the equals() method in BigDecimal can lead to unexpected results if not used carefully. In this blog post, I will show that comparing BigDecimals may be harder than you think.

Understanding BigDecimal

BigDecimal is a class in Java used to represent floating-point numbers with arbitrary precision. It consists of an unscaled value (BigInteger) and a scale (int), which represents the 10-based exponent. For example, the BigDecimal value 0.1234 has an unscaled value of 1,234 and a scale of 4 (0.1234 = 1234 × 10^-4).

The Equality Trap

One common mistake developers make is assuming that the equals() method in BigDecimal compares the numerical values. However, equals() actually compares the unscaled value and the scale, rather than the numerical equivalence. This can lead to surprising results when comparing BigDecimals with different scales.

Let's consider an example:

BigDecimal zero1 = new BigDecimal("0");
BigDecimal zero2 = new BigDecimal("0.0");
System.out.println(zero1.scale()); // Output: 0
System.out.println(zero2.scale()); // Output: 1
System.out.println(zero1.equals(zero2)); // Output: false

In this case we pass a Java String to the BigDecimal constructor to create a BigDecimal objects, but even though both zero1 and zero2 represent the value zero, their scales differ. As a result, the equals() method returns false, indicating that they are not equal.

Avoiding the Pitfall

To avoid this pitfall and compare BigDecimals based on their numerical values, you have a couple of options:

  1. Use the compareTo() method: The compareTo() method compares the numerical values of BigDecimals, regardless of their scales. It returns 0 if the numerical values are equal. Here's an example:

    BigDecimal value1 = new BigDecimal("1.23");
    BigDecimal value2 = new BigDecimal("1.230");
    System.out.println(value1.compareTo(value2)); // Output: 0
    
  2. Normalize the BigDecimals using stripTrailingZeros(): If you still need to use the equals() method, you can normalize the BigDecimals by removing trailing zeros using the stripTrailingZeros() method. This method creates a new BigDecimal object with the minimum possible scale without losing precision. Here's an example:

    BigDecimal zero1 = new BigDecimal("0").stripTrailingZeros();
    BigDecimal zero2 = new BigDecimal("0.0").stripTrailingZeros();
    System.out.println(zero1.scale()); // Output: 0
    System.out.println(zero2.scale()); // Output: 0
    System.out.println(zero1.equals(zero2)); // Output: true
    

    After stripping the trailing zeros, both zero1 and zero2 have the same scale, and the equals() method correctly identifies them as equal.

Conclusion

Comparing BigDecimals in Java can be tricky due to the behavior of the equals() method. By understanding the pitfalls and using the appropriate comparison techniques, such as compareTo() or normalizing the BigDecimals, you can ensure accurate comparisons based on numerical values. Keep these considerations in mind when working with BigDecimals to avoid unexpected results in your Java programs.

Follow me for more Java content