前言
在電商系統,金額計算的精確性和安全性至關重要。由於浮點數的固有特性,使用傳統的float
或double
型別進行貨幣計算可能會導致精度丟失,從而引發一系列問題。爲了解決這一問題,Java中的BigDecimal
類應運而生,成爲了處理金額計算的首選工具,並且在面試中,經常也會被問到下面這些問題,一定涉及過金額處理的操作吧?請問你用那個型別?精度如何處理的?有沒有碰到過坑?
為什麼選擇BigDecimal?
BigDecimal
類提供了任意精度的帶符號十進制數,非常適合需要精確小數點運算的場景。與float
和double
不同,BigDecimal
物件是不可變的,並且其精度和舍入模式都是可控的,這使得它在金融計算中具有顯著優勢。 可以看這個例子,就知道為什麼不用double。
private static void doubleDemo() { double amount1 = 0.03; double amount2 = 0.02; //執行結果會是多少? // 0.009999999999999998 System.out.println(amount1 - amount2); }
輸出結果是:
精度控制
在使用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();
執行結果如下:
等值比較
在進行金額比較時,應避免使用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 的 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)); //這種模式也就是我們常說的我們的 “四捨五入”。 }
執行結果:
資料庫設計建議
在設計資料庫表時,金額欄位應使用DECIMAL
型別而非INT
型別。DECIMAL
型別可以指定小數點後的位數,確保數值的精確儲存。例如,DECIMAL(10, 2)
可以儲存最多兩位小數的數值,總長度為10位。
結論
在處理金額計算時,BigDecimal
類提供了必要的精確性和安全性。透過合理使用BigDecimal
,可以避免由於浮點數精度問題導致的計算錯誤,確保金融系統的可靠性和穩定性。無論是電商平臺的支付模組,還是金融應用中的交易系統,正確使用BigDecimal
都是保障業務順利進行的關鍵
作者:小明愛吃火鍋
連結:https://juejin.cn/post/7437054131557564456