Java 中比較 BigDecimal 的陷阱

banq發表於2024-06-18

在 Java 中使用浮點數時,開發人員經常求助於 BigDecimal 類進行精確計算。但是,如果使用不當,BigDecimal 中的 equals() 方法的行為可能會導致意外結果。在這篇博文中,我將說明比較 BigDecimals 可能比您想象的要難。

理解BigDecimal
BigDecimal 是 Java 中的一個類,用於表示任意精度的浮點數。它由一個非標度值 (BigInteger) 和一個標度 (int) 組成,後者表示以 10 為基數的指數。例如,BigDecimal 值 0.1234 的非標度值為 1,234,標度為 4(0.1234 = 1234 × 10^-4)。

陷阱
開發人員常犯的一個錯誤是假設 BigDecimal 中的 equals() 方法會比較數值。然而,equals() 實際上會比較非標度值和標度,而不是數值等價性。當比較具有不同標度的 BigDecimal 時,這可能會導致令人驚訝的結果。

BigDecimal zero1 = new BigDecimal(<font>"0");
BigDecimal zero2 = new BigDecimal(
"0.0");
System.out.println(zero1.scale());
// Output: 0<i>
System.out.println(zero2.scale());
// Output: 1<i>
System.out.println(zero1.equals(zero2));
// Output: false<i>

在本例中,我們向 BigDecimal 建構函式傳遞了一個 Java 字串,以建立一個 BigDecimal 物件,但儘管 zero1 和 zero2 都表示值 0,它們的刻度卻不同。因此,equals() 方法返回 false,表明它們不相等。

解決方法
要避免這個陷阱,並根據 BigDecimals 的數值對其進行比較,您有幾個選擇:

1、使用 compareTo() 方法:
compareTo() 方法比較 BigDecimals 的數值,而不考慮它們的比例。如果數值相等,則返回 0。下面是一個示例:

BigDecimal value1 = new BigDecimal(<font>"1.23");
BigDecimal value2 = new BigDecimal(
"1.230");
System.out.println(value1.compareTo(value2));
// Output: 0<i>

2、使用 stripTrailingZeros() 將 BigDecimals 歸一化:
如果仍然需要使用 equals() 方法,可以使用 stripTrailingZeros() 方法去除尾數零,從而將 BigDecimals 歸一化。該方法將建立一個新的 BigDecimal 物件,並在不降低精度的前提下儘可能減小刻度。下面是一個示例:

BigDecimal zero1 = new BigDecimal(<font>"0").stripTrailingZeros();
BigDecimal zero2 = new BigDecimal(
"0.0").stripTrailingZeros();
System.out.println(zero1.scale());
// Output: 0<i>
System.out.println(zero2.scale());
// Output: 0<i>
System.out.println(zero1.equals(zero2));
// Output: true<i>

去掉尾部的零後,zero1 和 zero2 的刻度相同,equals() 方法可以正確地將它們識別為相等。

結論
由於 equals() 方法的行為,在 Java 中比較 BigDecimals 可能很棘手。透過了解這些陷阱並使用適當的比較技術(如 compareTo() 或對 BigDecimals 進行規範化處理),您可以確保基於數值的比較準確無誤。在使用 BigDecimals 時,請牢記這些注意事項,以避免在 Java 程式中出現意外結果。

相關文章