【BUG解决】关于BigDecimal与0的比较问题
这是一个很细小的知识点,但是很容易被忽略掉,导致系统问题,因此记录下来
问题背景
明明逻辑上看a和b都不为0才会调用除法,但是系统会报错:java.lang.ArithmeticException
异常:
if (!a.equals(BigDecimal.ZERO) && !b.equals(BigDecimal.ZERO)) {BigDecimal divide = a.divide(b, 2, RoundingMode.HALF_UP);
}
异常截图:
分析
说明 a.equals(BigDecimal.ZERO)
和 b.equals(BigDecimal.ZERO)
同时为false,但是能引起java.lang.ArithmeticException
异常的,b为0,所以 b.equals(BigDecimal.ZERO)
的判断有误
然后就考虑到如果b的精度不是0,而是0.00,那么 b.equals(BigDecimal.ZERO)
是不是会为false?
在 Java 中,BigDecimal 的 equals 方法比较的是值及其精度。
这是因为 BigDecimal.ZERO
表示的数字是 0, 精度(scale)为 0(即没有小数部分)。而 0.00 的精度则为 2,所以它们被认为是不同的对象。以下是示例代码:
BigDecimal b = new BigDecimal("0.00");
System.out.println(b.equals(BigDecimal.ZERO)); // 输出: false
如果想检查一个 BigDecimal 是否为零而不关心精度,使用 compareTo 方法,如下所示:
if (b.compareTo(BigDecimal.ZERO) == 0) {System.out.println("b 是零");
} else {System.out.println("b 不是零");
}
使用 compareTo 方法可以比较值而不考虑精度,这样,对于 0.00 和 0 的比较都是等于零的。
源码分析
这里我们对equals和compareTo的源码进行分析
(1) BigDecimal.equals()
的实现会比较数值和精度(scale):
public boolean equals(Object x) {if (!(x instanceof BigDecimal))return false;BigDecimal xDec = (BigDecimal) x;if (x == this)return true;if (scale != xDec.scale) // 精度比较return false;long s = this.intCompact;long xs = xDec.intCompact;if (s != INFLATED) {if (xs == INFLATED)xs = compactValFor(xDec.intVal);return (xs == s);} else if (xs != INFLATED)return compactValFor(this.intVal) == xs;return this.inflated().equals(xDec.inflated());
}
关键点:
首先比较精度(scale),如果不相同直接返回false
然后比较数值本身
所以 new BigDecimal("0.00").equals(BigDecimal.ZERO)
会返回false,因为精度不同(2 vs 0)
(2) compareTo 方法源码分析
BigDecimal.compareTo()
的实现:
public int compareTo(BigDecimal val) {// Quick path for equal scale and non-inflated caseif (scale == val.scale) {long xs = intCompact;long ys = val.intCompact;if (xs != INFLATED && ys != INFLATED)return xs != ys ? ((xs > ys) ? 1 : -1) : 0;}int xsign = this.signum();int ysign = val.signum();if (xsign != ysign)return (xsign > ysign) ? 1 : -1;if (xsign == 0)return 0;int cmp = compareMagnitude(val);return (xsign > 0) ? cmp : -cmp;
}
首先会比较符号(signum)
然后使用 compareMagnitude 比较绝对值
不直接比较精度(scale),而是会统一调整后再比较
new BigDecimal("0.00").compareTo(BigDecimal.ZERO)
会返回0,因为数值相同
最佳实践
当需要严格比较两个BigDecimal是否完全相同时(包括精度),使用 equals()
当只需要比较数值大小时,使用 compareTo()
检查是否为0时,推荐使用 compareTo()