<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 3983字,需瀏覽 8分鐘

           ·

          2022-06-09 01:26

          Hollis的新書限時折扣中,一本深入講解Java基礎(chǔ)的干貨筆記!

          來源:juejin.cn/post/7087404273503305736


          # 背景


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


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


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


          【問題描述】


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


          【事故級別】


          P0


          【過程】


          • 13:44 接到報警,訂單支付失敗,支付可用率降至60%

          • 13:50 迅速回滾上線代碼,恢復正常;

          • 14:20 review代碼,預發(fā)布驗證發(fā)現(xiàn)問題點

          • 14:58 修改問題代碼上線,線上恢復


          【故障原因】 BigDecimal在金額計算中丟失精度


          # 原因分析


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

          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);}

          執(zhí)行結(jié)果如下:



          通過測試發(fā)現(xiàn),當使用double或者float這些浮點數(shù)據(jù)類型時,會丟失精度,String、int則不會;這是為什么呢?


          我們點開構(gòu)造器方法看下源碼:

          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對應(yīng))中提供了double與long轉(zhuǎn)換,doubleToRawLongBits就是將double轉(zhuǎn)換為long,這個方法是原始方法(底層不是java實現(xiàn),是c++實現(xiàn)的)。


          double之所以會出問題,是因為小數(shù)點轉(zhuǎn)二進制丟失精度。BigDecimal在處理的時候把十進制小數(shù)擴大N倍讓它在整數(shù)上進行計算,并保留相應(yīng)的精度信息



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


          2. 并沒有提供完全精確的結(jié)果,所以不應(yīng)該被用于精確的結(jié)果的場合。


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


          4. 當十進制小數(shù)位轉(zhuǎn)換二進制的時候也會出現(xiàn)無限循環(huán)或者超過浮點數(shù)尾數(shù)的長度。


          # 總結(jié)


          所以,在涉及到精度計算的過程中,我們盡量使用String類型來進行轉(zhuǎn)換,正確用法如下:

          BigDecimal bigDecimal2=new BigDecimal("8.8");BigDecimal bigDecimal3=new BigDecimal("8.812");System.out.println( bigDecimal2.compareTo(bigDecimal3));System.out.println( bigDecimal2.add(bigDecimal3));

          BigDecimal創(chuàng)建出來的是對象,我們不能用傳統(tǒng)的加減乘除對其進行運算,必須使用他的方法,在我們數(shù)據(jù)庫存儲里,如果我們使用的是double或者float類型,需要進行來回的轉(zhuǎn)換后進行計算,非常不方便。

          # 工具分享


          所以在這里整理出一個util類供大家使用。

          import?java.math.BigDecimal;
          /** * @Author shuaige * @Date 2022/4/17 * @Version 1.0 **/public class BigDecimalUtils { public static BigDecimal doubleAdd(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2); } public static BigDecimal floatAdd(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); return b1.add(b2); } public static BigDecimal doubleSub(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2); } public static BigDecimal floatSub(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); return b1.subtract(b2); }

          public static BigDecimal doubleMul(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2); } public static BigDecimal floatMul(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); return b1.multiply(b2); }

          public static BigDecimal doubleDiv(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); // 保留小數(shù)點后兩位 ROUND_HALF_UP = 四舍五入 return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP); } public static BigDecimal floatDiv(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); // 保留小數(shù)點后兩位 ROUND_HALF_UP = 四舍五入 return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP); } /** * 比較v1 v2大小 * @param v1 * @param v2 * @return v1>v2 return 1 v1=v2 return 0 v1 */ public static int doubleCompareTo(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.compareTo(b2); } public static int floatCompareTo(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); return b1.compareTo(b2); }}




          我的新書《深入理解Java核心技術(shù)》已經(jīng)上市了,上市后一直蟬聯(lián)京東暢銷榜中,目前正在6折優(yōu)惠中,想要入手的朋友千萬不要錯過哦~長按二維碼即可購買~


          長按掃碼享受6折優(yōu)惠



          往期推薦

          一文搞明白分布式事務(wù)解決方案!真的 so easy!


          祖?zhèn)鞔a如何優(yōu)化性能?


          搜狐員工遭遇“工資補助”詐騙?網(wǎng)友都蚌埠住了




          有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)

          歡迎大家關(guān)注Java之道公眾號


          好文章,我在看??

          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲AV无码成人专区 | 人妻AV在线观看 | 人人操人人妻人人干 | 内射学生妹网站 | 日韩一级电影在线观看 |