7

What is the simplest way to reduce a Java BigDecimal containing an arbitrary value to a canonical form so that two BigDecimal's representing the same number will compare equal using the equals() method?

I am parsing my numbers from arbitrary strings using code like this:

BigDecimal x = new BigDecimal(string1, MathContext.DECIMAL64);
BigDecimal y = new BigDecimal(string2, MathContext.DECIMAL64);

Since (string1, string2) are arbitrary, they could be, e.g., ("1", "1.0000") or ("-32.5", "1981")...

What I'm looking for is the simplest (shortest/cleanest code) implementation of the method canonicalize for which the above assertion

assert x.compareTo(y) != 0 ||
    (canonicalize(x).equals(canonicalize(y)) && 
     x.compareTo(canonicalize(x)) == 0 && y.compareTo(canonicalize(y)) == 0);

will succeed...:

public static BigDecimal canonicalize(BigDecimal b) {
    // TODO:
}
0xbe5077ed
  • 4,217
  • 1
  • 28
  • 62
  • 5
    Why would you need this instead of simply using `compareTo`? – Ted Hopp Oct 13 '14 at 20:59
  • 1
    Can't you use `x.setScale(y.scale())`? – Keppil Oct 13 '14 at 21:01
  • @Keppil, I'm looking at the docs now and it looks like you're right: [" Returns a `BigDecimal` whose scale is the specified value, and whose value is numerically equal to this `BigDecimal`'s."](http://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#setScale-int-) – 0xbe5077ed Oct 13 '14 at 21:05
  • You might run into rounding issues though. This might be a problem or might not, I'm not sure about the use case here. – Keppil Oct 13 '14 at 21:09
  • http://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#compareTo-java.math.BigDecimal- – President James K. Polk Oct 13 '14 at 21:37

2 Answers2

3

If you want to know if two BigDecimals are equal regardless of scale, just use .compareTo()

public static boolean bigDecimalEquals(BigDecimal b1, BigDecimal b2) {
    return b1.compareTo(b1) == 0;
}

It specifically recommends this in the Javadoc

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. This method is provided in preference to individual methods for each of the six boolean comparison operators (<, ==, >, >=, !=, <=).


If you actually want to convert the BigDecimal so that .equals() will work, just use the setScale method.

durron597
  • 30,764
  • 16
  • 92
  • 150
  • accepted for the reference to `setScale()` as mentioned by @Keppil earlier. It should be obvious from my original post that I know about `compareTo()` and didn't require that. – 0xbe5077ed Oct 14 '14 at 00:33
1

Use stripTrailingZeros(). This returns an equivalent BigDecimal with the scale minimized and is the simplest way to get a canonical representation. It's better than setScale() because it avoids any issues with rounding.

Sam
  • 1,200
  • 1
  • 9
  • 30