<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>

          踩坑了,P0事故造成訂單無法支付。。。

          共 4038字,需瀏覽 9分鐘

           ·

          2022-06-09 19:46

          點擊上方[全棧開發(fā)者社區(qū)]右上角[...][設(shè)為星標(biāo)?

          點擊領(lǐng)取全棧資料全棧資料

          目錄

          • 背景
          • 事故
          • 分析
          • 總結(jié)
          • 工具分享

          背景

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

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

          事故

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

          問題描述

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

          事故級別

          P0

          事故過程

          如下:

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

          • 13:50,迅速回滾上線代碼,恢復(fù)正常

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

          • 14:58,修改問題代碼上線,線上恢復(fù)

          故障原因

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

          原因分析

          首先我們先用一段代碼復(fù)現(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),當(dāng)使用 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)二進(jìn)制丟失精度。

          BigDecimal 在處理的時候把十進(jìn)制小數(shù)擴(kuò)大 N 倍讓它在整數(shù)上進(jìn)行計算,并保留相應(yīng)的精度信息。

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

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

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

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

          總結(jié)

          所以,在涉及到精度計算的過程中,我們盡量使用 String 類型來進(jìn)行轉(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)的加減乘除對其進(jìn)行運(yùn)算,必須使用他的方法,在我們數(shù)據(jù)庫存儲里,如果我們使用的是double或者float類型,需要進(jìn)行來回的轉(zhuǎn)換后進(jì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);
          ????}
          }
          • 覺得本文對你有幫助?請分享給更多人

            關(guān)注「全棧開發(fā)者社區(qū)」加星標(biāo),提升全棧技能

            本公眾號會不定期給大家發(fā)福利,包括送書、學(xué)習(xí)資源等,敬請期待吧!

            如果感覺推送內(nèi)容不錯,不妨右下角點個在看轉(zhuǎn)發(fā)朋友圈或收藏,感謝支持。

            好文章,留言、點贊、在看和分享一條龍


          瀏覽 38
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <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无码 | 91嫩草欧美久久久九九九 | 特级西西444www大胆免费看 | 在线黄免费 |