切換語言為:簡體

金額計算工具類 BigDecimal 詳解及容易出現的問題彙總

  • 爱糖宝
  • 2024-11-14
  • 2026
  • 0
  • 0

前言

在電商系統,金額計算的精確性和安全性至關重要。由於浮點數的固有特性,使用傳統的floatdouble型別進行貨幣計算可能會導致精度丟失,從而引發一系列問題。爲了解決這一問題,Java中的BigDecimal類應運而生,成爲了處理金額計算的首選工具,並且在面試中,經常也會被問到下面這些問題,一定涉及過金額處理的操作吧?請問你用那個型別?精度如何處理的?有沒有碰到過坑?

為什麼選擇BigDecimal?

BigDecimal類提供了任意精度的帶符號十進制數,非常適合需要精確小數點運算的場景。與floatdouble不同,BigDecimal物件是不可變的,並且其精度和舍入模式都是可控的,這使得它在金融計算中具有顯著優勢。 可以看這個例子,就知道為什麼不用double。

private static void doubleDemo() {
    double amount1 = 0.03;
    double amount2 = 0.02;
    //執行結果會是多少?
    // 0.009999999999999998
    System.out.println(amount1 - amount2);

}

輸出結果是:

金額計算工具類 BigDecimal 詳解及容易出現的問題彙總

精度控制

在使用BigDecimal時,可以透過建構函式或靜態工廠方法valueOf來建立物件。值得注意的是,直接使用double型別的值來構造BigDecimal可能會導致精度損失。因此,推薦使用String型別的引數來建立BigDecimal例項,以確保數值的精確表示。

BigDecimal amount1 = new BigDecimal(0.03);
BigDecimal amount2 = new BigDecimal(0.02);
//12.【強制】禁止使用構造方法BigDecimal(double) 的方式把double值轉化為BigDecimal物件,非要轉那?
System.out.println("amount1: " + amount1);
System.out.println("amount2: " + amount2);
//應該等於0.01
System.out.println(amount1.subtract(amount2));
System.out.println();

執行結果如下:

金額計算工具類 BigDecimal 詳解及容易出現的問題彙總

等值比較

在進行金額比較時,應避免使用equals方法,因為它不僅比較數值,還比較精度。相反,應該使用compareTo方法,該方法僅比較數值大小,更適合金融計算的需求。

private static void m2() {
    BigDecimal amount1 = new BigDecimal("0.9");
    BigDecimal amount2 = new BigDecimal("0.90");
   
    System.out.println("equals比較結果:" + amount1.equals(amount2));
    System.out.println("compareTo比較結構:" + amount1.compareTo(amount2));
}

執行結果:

金額計算工具類 BigDecimal 詳解及容易出現的問題彙總

除法運算

BigDecimal的除法運算需要特別注意,因為它可能產生無限迴圈小數。爲了避免這種情況,需要指定結果的精度和舍入模式。 BigDecimal 的 8 種 RoundingMode(舍入模式),下面兩種為常見模式:

  • ROUND_HALF_UP 向 “最接近” 的數字舍入,如果與兩個相鄰數字的距離相等,則為向上舍入的舍入模式。 如果捨棄部分 >= 0.5,則舍入行為與 ROUND_UP 相同;否則舍入行為與 ROUND_DOWN 相同。 這種模式也就是我們常說的我們的 “四捨五入”。

  • ROUND_HALF_DOWN 向 “最接近” 的數字舍入,如果與兩個相鄰數字的距離相等,則為向下舍入的舍入模式。 如果捨棄部分 > 0.5,則舍入行為與 ROUND_UP 相同;否則舍入行為與 ROUND_DOWN 相同。 這種模式也就是我們常說的我們的 “五舍六入”。 案例程式碼如下:

private static void m3() {
    BigDecimal amount1 = new BigDecimal("2.0");
    BigDecimal amount2 = new BigDecimal("3.0");
    //System.out.println(amount1.divide(amount2)); //Non-terminating decimal expansion; no exact representable decimal result.
    //我們該如何處理?
    System.out.println(amount1.divide(amount2, 2, RoundingMode.HALF_UP)); //這種模式也就是我們常說的我們的 “四捨五入”。
}

執行結果:

金額計算工具類 BigDecimal 詳解及容易出現的問題彙總

資料庫設計建議

在設計資料庫表時,金額欄位應使用DECIMAL型別而非INT型別。DECIMAL型別可以指定小數點後的位數,確保數值的精確儲存。例如,DECIMAL(10, 2)可以儲存最多兩位小數的數值,總長度為10位。

結論

在處理金額計算時,BigDecimal類提供了必要的精確性和安全性。透過合理使用BigDecimal,可以避免由於浮點數精度問題導致的計算錯誤,確保金融系統的可靠性和穩定性。無論是電商平臺的支付模組,還是金融應用中的交易系統,正確使用BigDecimal都是保障業務順利進行的關鍵


作者:小明愛吃火鍋
連結:https://juejin.cn/post/7437054131557564456

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.