<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    BigDecimal使用不當,造成P0事故!

    VSole2022-06-13 16:59:09

    文章來源:https://c1n.cn/MSqAy


    目錄

    • 背景
    • 事故
    • 分析
    • 總結
    • 工具分享


    背景



    我們在使用金額計算或者展示金額的時候經常會使用 BigDecimal,也是涉及金額時非常推薦的一個類型。


    BigDecimal 自身也提供了很多構造器方法,這些構造器方法使用不當可能會造成不必要的麻煩甚至是金額損失,從而引起事故資損。


    事故



    接下來我們看下收銀臺出的一起事故。


    | 問題描述

    收銀臺計算商品金額報錯,導致訂單無法支付。


    | 事故級別

    P0


    | 事故過程

    如下:

    • 13:44,接到報警,訂單支付失敗,支付可用率降至 60%
    • 13:50,迅速回滾上線代碼,恢復正常
    • 14:20,review 代碼,預發布驗證發現問題點
    • 14:58,修改問題代碼上線,線上恢復


    | 故障原因

    BigDecimal 在金額計算中丟失精度。


    原因分析



    首先我們先用一段代碼復現問題根源,如下所示:

    public static void main(String[] args) {
        BigDecimal bigDecimal=new BigDecimal(88);
        System.out.println(bigDecimal);
        bigDecimal=new BigDecimal("8.8");
        System.out.println(bigDecimal);
        bigDecimal=new BigDecimal(8.8);
        System.out.println(bigDecimal);
    }
    


    執行結果如下:

    通過測試發現,當使用 double 或者 float 這些浮點數據類型時,會丟失精度,String、int 則不會,這是為什么呢?


    我們點開構造器方法看下源碼:

    public static long doubleToLongBits(double value) {
        long result = doubleToRawLongBits(value);
        // Check for NaN based on values of bit fields, maximum
        // exponent and nonzero significand.
        if ( ((result & DoubleConsts.EXP_BIT_MASK) ==
              DoubleConsts.EXP_BIT_MASK) &&
             (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L)
            result = 0x7ff8000000000000L;
        return result;
    }
    


    問題就處在 doubleToRawLongBits 這個方法上,在 jdk 中 double 類(float 與 int 對應)中提供了 double 與 long 轉換,doubleToRawLongBits 就是將 double 轉換為 long,這個方法是原始方法(底層不是 java 實現,是 c++ 實現的)。


    double 之所以會出問題,是因為小數點轉二進制丟失精度。

    BigDecimal 在處理的時候把十進制小數擴大 N 倍讓它在整數上進行計算,并保留相應的精度信息。


    float 和 double 類型,主要是為了科學計算和工程計算而設計的,之所以執行二進制浮點運算,是為了在廣泛的數值范圍上提供較為精確的快速近和計算。


    并沒有提供完全精確的結果,所以不應該被用于精確的結果的場合。


    當浮點數達到一定大的數,就會自動使用科學計數法,這樣的表示只是近似真實數而不等于真實數。


    當十進制小數位轉換二進制的時候也會出現無限循環或者超過浮點數尾數的長度。


    總結

    所以,在涉及到精度計算的過程中,我們盡量使用 String 類型來進行轉換。

    bigdecimal
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    我們在使用金額計算或者展示金額的時候經常會使用 BigDecimal,也是涉及金額時非常推薦的一個類型。 BigDecimal 自身也提供了很多構造器方法,這些構造器方法使用不當可能會造成不必要的麻煩甚至是金額損失,從而引起事故資損。 事故 接下來我們看下收銀臺出的一起事故。 | 問題描述 收銀臺計算商品金額報錯,導致訂單無法支付。
    //把Map的購物車轉換為Item列表??//應付總價=商品總價+運費總價-總優惠??然后實現針對 VIP 用戶的購物車邏輯。所以,這部分代碼只需要額外處理多買折扣部分:public?
    業務同學抱怨業務開發沒有技術含量,用不到設計模式、Java 高級特性、OOP,平時寫代碼都在堆?CRUD,個人成長無從談起。其實,我認為不是這樣的。設計模式、OOP 是前輩們在大型項目中積累下來的經驗,通過這些方法論來改善大型項目的可維護性。
    Java線程安全:狹義地認為是多線程之間共享數據的訪問。 Java語言中各種操作共享的數據有5種類型:不可變、絕對線程安全、相對線程安全、線程兼容、線程獨立
    16 條代碼規范
    2022-01-18 07:34:07
    如何更規范化編寫Java 代碼 Many of the happiest people are those who own the least. But are we really so happy with our IPhones, our big houses, our fancy cars? 忘川如斯,擁有一切的人才更怕失去。 背景:如何更規范化編寫Java 代碼的重要性想必毋需多言,
    早在今年4月 Weblogic發布了安全公告,里面有一個編號是CVE-2021-2135的反序列化漏洞,因為工作原因需要構造該漏洞POC,當時拿到了安全補丁,但是奈何太菜并沒有解出來。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类