金融系統中正確的金額計算及存儲方式

經典的精度丟失問題
Java中的類型float、double用來做計算會有精度丟失問題,下面來看下面的示例。

public static void main(String[] args) {
    test1();
    test2();
}

private static void test1() {
    double totalAmount = 0.09;
    double feeAmount = 0.02;
    double tradeAmount = totalAmount - feeAmount;
    System.out.println(tradeAmount);
}

上面的程序輸出結果是多少?

0.07?非也!

正確的結果是:

0.06999999999999999
爲什麼是這樣?

浮點數可能丟失精度,浮點十進制數通常沒有完全相同的二進制的表示形式,這是CPU所採用的浮點數據表示形式的副作用。爲此,可能會有一些精度丟失,並且一些浮點運算可能會產生未知的結果。

浮點運算很少是精確的,只要是超過精度能表示的範圍就會產生誤差。所以,在使用float、double作精確運算的時候一定要特別小心,除非能容忍精度丟失,不然產生的誤差也是會造成雙方對賬不一致的結果。

怎麼解決
在《Effective Java》這本書中也提到這個原則,float和double只能用來做科學計算或者是工程計算,在商業計算中我們要用 java.math.BigDecimal。

BigDecimal適合更精度的運算,也提供了豐富的操作符類型,小數位控制,四捨五入規則等。

不過,使用BigDecimal不當也有精度丟失的情況,如double的構造方法:

BigDecimal(double val)
再來看這個示例:

private static void test2() {
    double totalAmount = 0.09;
    double feeAmount = 0.02;
    BigDecimal tradeAmount = new BigDecimal(totalAmount).subtract(new BigDecimal(feeAmount));
    System.out.println(tradeAmount);
}

輸出:

0.0699999999999999962529972918900966760702431201934814453125
這個精度就更恐怖了。。

所以,一定要使用String的構造方法:

BigDecimal(String val)

private static void test3() {
    double totalAmount = 0.09;
    double feeAmount = 0.02;
    BigDecimal tradeAmount = new BigDecimal(String.valueOf(totalAmount))
            .subtract(new BigDecimal(String.valueOf(feeAmount)));
    System.out.println(tradeAmount);
}

總結
金額運算儘量使用BigDecimal(String val)進行運算。

數據庫存儲金額,一般有整型和浮點型兩種存儲方式。如果是有匯率轉換的,建議使用浮點數decimal進行存儲,可以靈活的控制精度,decimal直接對應java類型BigDecimal。當然,用整數存儲分這種形式也可以,轉賬的時候單位爲元而如果忘了轉換分爲元,那就悲劇了。

原文:https://blog.csdn.net/youanyyou/article/details/78990100 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章