<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

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

    共 4038字,需瀏覽 9分鐘

     ·

    2022-06-09 19:46

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

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

    目錄

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

    背景

    我們?cè)谑褂媒痤~計(jì)算或者展示金額的時(shí)候經(jīng)常會(huì)使用 BigDecimal,也是涉及金額時(shí)非常推薦的一個(gè)類型。

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

    事故

    接下來(lái)我們看下收銀臺(tái)出的一起事故。

    問(wèn)題描述

    收銀臺(tái)計(jì)算商品金額報(bào)錯(cuò),導(dǎo)致訂單無(wú)法支付。

    事故級(jí)別

    P0

    事故過(guò)程

    如下:

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

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

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

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

    故障原因

    BigDecimal 在金額計(jì)算中丟失精度。

    原因分析

    首先我們先用一段代碼復(fù)現(xiàn)問(wè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é)果如下:

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

    我們點(diǎn)開(kāi)構(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;??
    }??

    問(wèn)題就處在 doubleToRawLongBits 這個(gè)方法上,在 jdk 中 double 類(float 與 int 對(duì)應(yīng))中提供了 double 與 long 轉(zhuǎn)換,doubleToRawLongBits 就是將 double 轉(zhuǎn)換為 long,這個(gè)方法是原始方法(底層不是 java 實(shí)現(xiàn),是 c++ 實(shí)現(xiàn)的)。

    double 之所以會(huì)出問(wèn)題,是因?yàn)樾?shù)點(diǎn)轉(zhuǎn)二進(jìn)制丟失精度。

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

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

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

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

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

    總結(jié)

    所以,在涉及到精度計(jì)算的過(guò)程中,我們盡量使用 String 類型來(lái)進(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)建出來(lái)的是對(duì)象,我們不能用傳統(tǒng)的加減乘除對(duì)其進(jìn)行運(yùn)算,必須使用他的方法,在我們數(shù)據(jù)庫(kù)存儲(chǔ)里,如果我們使用的是double或者float類型,需要進(jìn)行來(lái)回的轉(zhuǎn)換后進(jìn)行計(jì)算,非常不方便。

    工具分享

    所以在這里整理出一個(gè)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ù)點(diǎn)后兩位?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ù)點(diǎn)后兩位?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);
    ????}
    }
    • 覺(jué)得本文對(duì)你有幫助?請(qǐng)分享給更多人

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

      本公眾號(hào)會(huì)不定期給大家發(fā)福利,包括送書(shū)、學(xué)習(xí)資源等,敬請(qǐng)期待吧!

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

      好文章,留言、點(diǎn)贊、在看和分享一條龍


    瀏覽 38
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    特级黄色免费无码 | 国产精品久久久久久久久久梁医生 | 天天爱天天干天天 | 亚洲aⅴⅴ网站 | 夜夜高潮少妇 |