Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why BigDecimal("5.50") not equals to BigDecimal("5.5") and how to work around this issue?

Actually, I've found possible solution

//returns true
new BigDecimal("5.50").doubleValue() == new BigDecimal("5.5").doubleValue()

Of course, it can be improved with something like Math.abs (v1 - v2) < EPS to make the comparison more robust, but the question is whether this technique acceptable or is there a better solution?

If someone knows why java designers decided to implement BigDecimal's equals in that way, it would be interesting to read.

like image 348
Roman Avatar asked Oct 05 '10 18:10

Roman


People also ask

How does BigDecimal determine less than or equal?

Use the compareTo method of BigDecimal : public int compareTo(BigDecimal val) Compares this BigDecimal with the specified BigDecimal. Returns: -1, 0, or 1 as this BigDecimal is numerically less than, equal to, or greater than val.

How can I compare two BigDecimal values?

compareTo(BigDecimal val) compares the BigDecimal Object with the specified BigDecimal value. 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.

How do I assign a value to BigDecimal?

In order to add a BigDecimal to another BigDecimal, use add(BigDecimal augend) API method, that returns a BigDecimal whose value is (this + augend), and whose scale is max(this. scale(), augend.


3 Answers

From the javadoc of BigDecimal

equals

public boolean equals(Object x)

Compares this BigDecimal with the specified Object for equality. 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).

Simply use compareTo() == 0

like image 163
Colin Hebert Avatar answered Oct 04 '22 16:10

Colin Hebert


The simplest expression to compare ignoring trailing zeros is since Java 1.5:

bd1.stripTrailingZeros().equals(bd2.stripTrailingZeros())
like image 40
user1708042 Avatar answered Oct 04 '22 17:10

user1708042


Using == to compare doubles seems like a bad idea in general.

You could call setScale to the same thing on the numbers you're comparing:

new BigDecimal ("5.50").setScale(2).equals(new BigDecimal("5.5").setScale (2))

where you would be setting the scale to the larger of the two:

BigDecimal a1 = new BigDecimal("5.051");
BigDecimal b1 = new BigDecimal("5.05");
// wow, this is awkward in Java
int maxScale = Collections.max(new ArrayList() {{ a1.scale(), b1.scale()}});
System.out.println(
  a1.setScale(maxScale).equals(b1.setScale(maxScale)) 
  ? "are equal" 
  : "are different" );

Using compareTo() == 0 is the best answer, though. The increasing of the scale of one of the numbers in my approach above is likely the "unnecessary inflation" that the compareMagnitude method documentation is mentioning when it says:

/**
 * Version of compareTo that ignores sign.
 */
private int compareMagnitude(BigDecimal val) {
    // Match scales, avoid unnecessary inflation
    long ys = val.intCompact;
    long xs = this.intCompact;

and of course compareTo is a lot easier to use since it's already implemented for you.

like image 41
Nathan Hughes Avatar answered Oct 04 '22 17:10

Nathan Hughes