<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 為什么可以保證精度不丟失?

          共 8096字,需瀏覽 17分鐘

           ·

          2024-04-29 10:30

          來源:juejin.cn/post/7348709938023940136

          ?? 歡迎加入小哈的星球 ,你將獲得: 專屬的項(xiàng)目實(shí)戰(zhàn) / Java 學(xué)習(xí)路線 / 一對(duì)一提問 / 學(xué)習(xí)打卡 /  贈(zèng)書福利


          全棧前后端分離博客項(xiàng)目 2.0 版本完結(jié)啦, 演示鏈接http://116.62.199.48/ ,新項(xiàng)目正在醞釀中。全程手摸手,后端 + 前端全棧開發(fā),從 0 到 1 講解每個(gè)功能點(diǎn)開發(fā)步驟,1v1 答疑,直到項(xiàng)目上線。目前已更新了239小節(jié),累計(jì)38w+字,講解圖:1645張,還在持續(xù)爆肝中.. 后續(xù)還會(huì)上新更多項(xiàng)目,目標(biāo)是將Java領(lǐng)域典型的項(xiàng)目都整一波,如秒殺系統(tǒng), 在線商城, IM即時(shí)通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),已有1300+小伙伴加入(早鳥價(jià)超低)



          在金融領(lǐng)域,為了保證數(shù)據(jù)的精度,往往會(huì)使用BigDecimal。本文就來探討下為什么BigDecimal可以保證精度不丟失。

          類介紹

          首先來看一下BigDecimal的類聲明以及幾個(gè)屬性:

          public class BigDecimal extends Number implements Comparable<BigDecimal> {
              // 該BigDecimal的未縮放值
              private final BigInteger intVal;
              // 精度,可以理解成小數(shù)點(diǎn)后的位數(shù)
              private final int scale;
              // BigDecimal中的十進(jìn)制位數(shù),如果位數(shù)未知,則為0(備用信息)
              private transient int precision;
              // Used to store the canonical string representation, if computed.
              // 這個(gè)我理解就是存實(shí)際的BigDecimal值
              private transient String stringCache;
              // 擴(kuò)大成long型數(shù)值后的值
              private final transient long intCompact;
          }

          從例子入手

          通過debug來發(fā)現(xiàn)源碼中的奧秘是了解類運(yùn)行機(jī)制很好的方式。請(qǐng)看下面的testBigDecimal方法:

          @Test
          public void testBigDecimal() {
              BigDecimal bigDecimal1 = BigDecimal.valueOf(2.36);
              BigDecimal bigDecimal2 = BigDecimal.valueOf(3.5);
              BigDecimal resDecimal = bigDecimal1.add(bigDecimal2);
              System.out.println(resDecimal);
          }

          在執(zhí)行了BigDecimal.valueOf(2.36)后,查看debug信息可以發(fā)現(xiàn)上述提到的幾個(gè)屬性被賦了值:

          圖片

          接下來進(jìn)到add方法里面,看看它是怎么計(jì)算的:

          /**
           * Returns a BigDecimal whose value is (this + augend), 
           * and whose scale is max(this.scale(), augend.scale()).
           */
          public BigDecimal add(BigDecimal augend) {
              if (this.intCompact != INFLATED) {
                  if ((augend.intCompact != INFLATED)) {
                      return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
                  } else {
                      return add(this.intCompact, this.scale, augend.intVal, augend.scale);
                  }
              } else {
                  if ((augend.intCompact != INFLATED)) {
                      return add(augend.intCompact, augend.scale, this.intVal, this.scale);
                  } else {
                      return add(this.intVal, this.scale, augend.intVal, augend.scale);
                  }
              }
          }

          看一下傳進(jìn)來的值:

          圖片

          進(jìn)入第8行的add方法:

          private static BigDecimal add(final long xs, int scale1, final long ys, int scale2) {
              long sdiff = (long) scale1 - scale2;
              if (sdiff == 0) {
                  return add(xs, ys, scale1);
              } else if (sdiff < 0) {
                  int raise = checkScale(xs,-sdiff);
                  long scaledX = longMultiplyPowerTen(xs, raise);
                  if (scaledX != INFLATED) {
                      return add(scaledX, ys, scale2);
                  } else {
                      BigInteger bigsum = bigMultiplyPowerTen(xs,raise).add(ys);
                      return ((xs^ys)>=0) ? // same sign test
                          new BigDecimal(bigsum, INFLATED, scale2, 0)
                          : valueOf(bigsum, scale2, 0);
                  }
              } else {
                  int raise = checkScale(ys,sdiff);
                  long scaledY = longMultiplyPowerTen(ys, raise);
                  if (scaledY != INFLATED) {
                      return add(xs, scaledY, scale1);
                  } else {
                      BigInteger bigsum = bigMultiplyPowerTen(ys,raise).add(xs);
                      return ((xs^ys)>=0) ?
                          new BigDecimal(bigsum, INFLATED, scale1, 0)
                          : valueOf(bigsum, scale1, 0);
                  }
              }
          }

          這個(gè)例子中,該方法傳入的參數(shù)分別是:xs=236,scale1=2,ys=35,scale2=1

          該方法首先計(jì)算scale1 - scale2,根據(jù)差值走不同的計(jì)算邏輯,這里求出來是1,所以進(jìn)入到最下面的else代碼塊(這塊是關(guān)鍵):

          • 首先17行校驗(yàn)了一下數(shù)值范圍
          • 18行將ys擴(kuò)大了10的n次倍,這里n=raise=1,所以返回的scaledY=350
          • 接著就進(jìn)入到20行的add方法:
          private static BigDecimal add(long xs, long ys, int scale){
              long sum = add(xs, ys);
              if (sum!=INFLATED)
                  return BigDecimal.valueOf(sum, scale);
              return new BigDecimal(BigInteger.valueOf(xs).add(ys), scale);
          }

          這個(gè)方法很簡(jiǎn)單,就是計(jì)算和,然后返回BigDecimal對(duì)象:

          圖片

          結(jié)論

          所以可以得出結(jié)論:BigDecimal在計(jì)算時(shí),實(shí)際會(huì)把數(shù)值擴(kuò)大10的n次倍,變成一個(gè)long型整數(shù)進(jìn)行計(jì)算,整數(shù)計(jì)算時(shí)自然可以實(shí)現(xiàn)精度不丟失。同時(shí)結(jié)合精度scale,實(shí)現(xiàn)最終結(jié)果的計(jì)算。

          ?? 歡迎加入小哈的星球 ,你將獲得: 專屬的項(xiàng)目實(shí)戰(zhàn) / Java 學(xué)習(xí)路線 / 一對(duì)一提問 / 學(xué)習(xí)打卡 /  贈(zèng)書福利


          全棧前后端分離博客項(xiàng)目 2.0 版本完結(jié)啦, 演示鏈接http://116.62.199.48/ ,新項(xiàng)目正在醞釀中。全程手摸手,后端 + 前端全棧開發(fā),從 0 到 1 講解每個(gè)功能點(diǎn)開發(fā)步驟,1v1 答疑,直到項(xiàng)目上線。目前已更新了239小節(jié),累計(jì)38w+字,講解圖:1645張,還在持續(xù)爆肝中.. 后續(xù)還會(huì)上新更多項(xiàng)目,目標(biāo)是將Java領(lǐng)域典型的項(xiàng)目都整一波,如秒殺系統(tǒng), 在線商城, IM即時(shí)通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),已有1300+小伙伴加入(早鳥價(jià)超低)



               
                  

          1. 我的私密學(xué)習(xí)小圈子~

          2. 大廠都在用的 Git 代碼管理規(guī)范 !

          3. 架構(gòu)應(yīng)該如何來理解?

          4. Java8 Steam流太難用了?那你可以試試 JDFrame

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          PS:因公眾號(hào)平臺(tái)更改了推送規(guī)則,如果不想錯(cuò)過內(nèi)容,記得讀完點(diǎn)一下在看,加個(gè)星標(biāo),這樣每次新文章推送才會(huì)第一時(shí)間出現(xiàn)在你的訂閱列表里。

          點(diǎn)“在看”支持小哈呀,謝謝啦

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  国产视频一区在线播放 | 爱搞一区 | 免费A片国产毛无码A片牛牛 | 成人777777 | 午夜黄色电影 |