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

    BigDecimal使用不當(dāng),造成P0事故!

    共 3943字,需瀏覽 8分鐘

     ·

    2022-06-12 11:24

    來源:juejin.cn/post/7087404273503305736

    背景

    我們在使用金額計算或者展示金額的時候經(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)二進制丟失精度。BigDecimal在處理的時候把十進制小數(shù)擴大N倍讓它在整數(shù)上進行計算,并保留相應(yīng)的精度信息

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

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

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

    • 當(dāng)十進制小數(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?v1return?-1
    ?????*/
    ????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);
    ????}
    }

    *

    最后


    IDEA2022.1版本最近發(fā)布了,想要早點嘗鮮的小伙伴,可以去下載了

    ????點擊原文閱讀,可以獲取最新激活。

    瀏覽 41
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

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

    手機掃一掃分享

    分享
    舉報

    <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>
    色就是色欧美setu | 午夜影院无码 | 91影院理论推荐手机在线观看 | 亲女乱婬一级A片 | 国产成人三级在线 |