On the difference between equals and compareTo of BigDecimal in java

  • 2020-05-16 07:02:32
  • OfStack

There were some problems in the payment amount verification process these two days. I used the equals method of BigDecimal to compare the two amounts to see if they were equal. As a result, there were some errors in the amount comparison (such as the comparison between 3.0 and 3.00, etc.).

[note: the following are based on sun jdk version 1.4.2 for example, other versions may not be 1, please ignore]

First look at the equals method of BigDecimal:


public boolean equals(Object x){
	if (!(x instanceof BigDecimal))
	  return false;
	BigDecimal xDec = (BigDecimal) x;

	return scale == xDec.scale && intVal.equals(xDec.intVal);
  }

You can see that the euquals method of BigDecimal first determines the data type to be compared, and if the object type is 1, it determines whether the accuracy (scale) and the value (BigInteger's equals method) are 1.

In fact, javadoc is very clear: "Compares this BigDecimal with the specified Object for equality. Unlike compareTo, this method considers BigDecimal objects equal only if are and scale (thus 2.0 is not equal sun 2.00 when compared by this "I just didn't notice!

Let's look at the compareTo method again:


public int compareTo(BigDecimal val){
	/* Optimization: would run fine without the next three lines */
	int sigDiff = signum() - val.signum();
	if (sigDiff != 0)
	  return (sigDiff > 0 ? 1 : -1);

	/* If signs match, scale and compare intVals */
	BigDecimal arg[] = new BigDecimal[2];
	arg[0] = this;	arg[1] = val;
	matchScale(arg);
	return arg[0].intVal.compareTo(arg[1].intVal);
  }

You can see that there is an matchScale treatment in this method, which means to convert the object with low accuracy into one with high accuracy, and then compare it (the compareTo method of BigInteger). The implementation of matchScale is as follows:


private static void matchScale(BigDecimal[] val) {
	if (val[0].scale < val[1].scale)
	  val[0] = val[0].setScale(val[1].scale);
	else if (val[1].scale < val[0].scale)
	  val[1] = val[1].setScale(val[0].scale);
  }

Here's a simple test:


System.out.println(new BigDecimal("1.2").equals(new BigDecimal("1.20"))); // The output false

System.out.println(new BigDecimal("1.2").compareTo(new BigDecimal("1.20")) == 0); // The output true 

In addition, notice that the BigDecimal constructor above me passes in strings. What will happen if the number type is passed in? You can test 1 by yourself and then analyze 1 reason:


System.out.println(new BigDecimal("1.2").equals(new BigDecimal("1.20"))); // The output false
System.out.println(new BigDecimal("1.2").compareTo(new BigDecimal("1.20")) == 0); // The output true
    
System.out.println(new BigDecimal(1.2).equals(new BigDecimal("1.20"))); // The output is ?
System.out.println(new BigDecimal(1.2).compareTo(new BigDecimal("1.20")) == 0); // The output is ?
  
System.out.println(new BigDecimal(1.2).equals(new BigDecimal(1.20))); // The output is ?
System.out.println(new BigDecimal(1.2).compareTo(new BigDecimal(1.20)) == 0);// The output is ?

The final conclusion is that, for the size comparison of BigDecimal, the equals method will not only compare the size of the value, but also the accuracy of the two objects, while the compareTo method will not compare the accuracy, only the size of the value.

Finally despise 1 bottom oneself, have used so many years Java language, even basic common sense did not make clear!


Related articles: