這是一個很細小的知識點,但是很容易被忽略掉,導致系統問題,因此記錄下來
問題背景
明明邏輯上看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()