<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      BigDecimal的精度與刻度


      BigDecimal是Java中用于高精度算術(shù)運(yùn)算的類(lèi)。當(dāng)您需要精確地處理非常大或非常小的數(shù)字時(shí),例如在金融計(jì)算中,它特別有用。由于眾所周知得原因,Double這種類(lèi)型在某些情況下會(huì)出現(xiàn)丟失精度的問(wèn)題,所以在需要對(duì)較為敏感的數(shù)據(jù)(比如與金額有關(guān)的)進(jìn)行運(yùn)算時(shí),我們都會(huì)用BigDecimal。但是,用BigDecimal不代表就一定沒(méi)問(wèn)題,我們今天就討論一下關(guān)于BigDecimal的問(wèn)題。

      精度與刻度

      要正確使用BigDecimal,首先要清楚精度(precision)和刻度(scale)的概念。

      • Precision(精度):表示數(shù)值的總位數(shù),包括小數(shù)點(diǎn)前后的位數(shù)。例如,數(shù)值 123.45 的精度是 5,因?yàn)樗?5 位數(shù)字。

      • Scale(刻度):表示小數(shù)點(diǎn)后的位數(shù)。例如,數(shù)值 123.45 的刻度是 2,因?yàn)樾?shù)點(diǎn)后有 2 位數(shù)字。

      舉個(gè)例子,如果一個(gè)數(shù)值類(lèi)型定義為 DECIMAL(7, 2),那么它的精度是 7,刻度是 2。這意味著這個(gè)數(shù)值最多可以有 7 位數(shù)字,其中 2 位在小數(shù)點(diǎn)后,5 位在小數(shù)點(diǎn)前。
      (p.s. DECIMAL這個(gè)數(shù)值類(lèi)型通常是用在數(shù)據(jù)庫(kù)中的,JAVA中并沒(méi)有這個(gè)類(lèi)型。用這個(gè)例子是因?yàn)樗梢宰钋逦卣f(shuō)明精度與刻度)

      BigDecimal類(lèi)中也有獲取精度和刻度的方法

          BigDecimal num = new BigDecimal("12.1234");
      	System.out.println(String.format("precision:%s scale:%s", num.precision(),num.scale()));
      
          //輸出:precision:6 scale:4
      

      除法中的刻度

      在用BigDecimal做除法運(yùn)算,使用divide方法的時(shí)候,可以指定刻度,也可以不指定。

      當(dāng)指定刻度,即保留幾位小數(shù)的時(shí)候,需要指定進(jìn)位模式(RoundingMode)。
      可選的模式有UP、DOWN、CEILING、FLOOR、HALF_UP、HALF_DOWN、HALF_EVEN、UNNECESSARY。
      JDK api中用一個(gè)表格比較了這幾種模式的區(qū)別

      Result of rounding input to one digit with the given rounding mode

      Input Number UP DOWN CEILING FLOOR HALF_UP HALF_DOWN HALF_EVEN UNNECESSARY
      5.5 6 5 6 5 6 5 6 throw ArithmeticException
      2.5 3 2 3 2 3 2 2 throw ArithmeticException
      1.6 2 1 2 1 2 2 2 throw ArithmeticException
      1.1 2 1 2 1 1 1 1 throw ArithmeticException
      1.0 1 1 1 1 1 1 1 1
      -1.0 -1 -1 -1 -1 -1 -1 -1 -1
      -1.1 -2 -1 -1 -2 -1 -1 -1 throw ArithmeticException
      -1.6 -2 -1 -1 -2 -2 -2 -2 throw ArithmeticException
      -2.5 -3 -2 -2 -3 -3 -2 -2 throw ArithmeticException
      -5.5 -6 -5 -5 -6 -6 -5 -6 throw ArithmeticException

      按指定的規(guī)則進(jìn)位,保留幾位小數(shù),這沒(méi)有問(wèn)題。

      如果不指定刻度呢?

          BigDecimal one = new BigDecimal("1");
      	BigDecimal eight = new BigDecimal("8");
          System.out.println(one.divide(eight));//輸出 0.125
      	BigDecimal three = new BigDecimal("3");
          System.out.println(one.divide(three));// java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
      
      

      當(dāng)結(jié)果能除盡的時(shí)候正常處理,當(dāng)除不盡即結(jié)果是無(wú)限循環(huán)小數(shù)的時(shí)候,程序拋出異常。
      看一下源碼:

      public BigDecimal divide(BigDecimal divisor) {
              /*
               * Handle zero cases first.
               */
              if (divisor.signum() == 0) {   // x/0
                  if (this.signum() == 0)    // 0/0
                      throw new ArithmeticException("Division undefined");  // NaN
                  throw new ArithmeticException("Division by zero");
              }
      
              // Calculate preferred scale
              int preferredScale = saturateLong((long) this.scale - divisor.scale);
      
              if (this.signum() == 0) // 0/y
                  return zeroValueOf(preferredScale);
              else {
                  /*
                   * If the quotient this/divisor has a terminating decimal
                   * expansion, the expansion can have no more than
                   * (a.precision() + ceil(10*b.precision)/3) digits.
                   * Therefore, create a MathContext object with this
                   * precision and do a divide with the UNNECESSARY rounding
                   * mode.
                   */
                  MathContext mc = new MathContext( (int)Math.min(this.precision() +
                                                                  (long)Math.ceil(10.0*divisor.precision()/3.0),
                                                                  Integer.MAX_VALUE),
                                                    RoundingMode.UNNECESSARY);
                  BigDecimal quotient;
                  try {
                      quotient = this.divide(divisor, mc);
                  } catch (ArithmeticException e) {
                      throw new ArithmeticException("Non-terminating decimal expansion; " +
                                                    "no exact representable decimal result.");
                  }
      
                  int quotientScale = quotient.scale();
      
                  // divide(BigDecimal, mc) tries to adjust the quotient to
                  // the desired one by removing trailing zeros; since the
                  // exact divide method does not have an explicit digit
                  // limit, we can add zeros too.
                  if (preferredScale > quotientScale)
                      return quotient.setScale(preferredScale, ROUND_UNNECESSARY);
      
                  return quotient;
              }
          }
      

      MathContext mc = new MathContext( (int)Math.min(this.precision() +(long)Math.ceil(10.0*divisor.precision()/3.0),Integer.MAX_VALUE),RoundingMode.UNNECESSARY);

      這一句算了一個(gè)刻度,然后進(jìn)位方式選了UNNECESSARY。所以當(dāng)除不盡的時(shí)候就拋異常了。

      也就是說(shuō)它斷定,如果一個(gè)除法能除盡,它的小數(shù)位小于this.precision() +(long)Math.ceil(10.0*divisor.precision()/3.0)

      這句話(huà)的意思是,如果 this 除以 divisor 的商是一個(gè)有限小數(shù),那么這個(gè)小數(shù)的位數(shù)不會(huì)超過(guò)以下公式計(jì)算的結(jié)果:
      \(a.precision() + \lceil \frac{10 \cdot b.precision}{3} \rceil\)
      其中:
      ? a.precision() 是 this 的精度,即 this 的總位數(shù)。
      ? b.precision() 是 divisor 的精度,即 divisor 的總位數(shù)。
      ? ceil 是向上取整函數(shù)。
      這個(gè)公式的作用是估算有限小數(shù)的最大位數(shù)。具體來(lái)說(shuō),它考慮了 this 和 divisor 的精度,并通過(guò)一個(gè)比例關(guān)系來(lái)確定商的小數(shù)位數(shù)上限。
      舉個(gè)例子,如果 this 的精度是 5,divisor 的精度是 3,那么公式計(jì)算如下:
      \(5 + \lceil \frac{10 \cdot 3}{3} \rceil = 5 + \lceil 10 \rceil = 5 + 10 = 15\)
      因此,商的有限小數(shù)位數(shù)不會(huì)超過(guò) 15 位。

      插一句啊,這是為什么?

      AI是這樣回答的:

      這個(gè)公式的提出者并沒(méi)有明確的記錄,但它基于數(shù)論中的一些基本原理。證明這個(gè)公式需要用到基本算術(shù)定理(Fundamental Theorem of Arithmetic),即每個(gè)整數(shù)都可以唯一地分解為素?cái)?shù)的乘積。
      證明思路

      1. 有限小數(shù)的條件:
        一個(gè)分?jǐn)?shù) \(\frac{a}{b}\)只有在其分母 (b) 僅包含 2 和 5 作為素因子時(shí),才會(huì)有一個(gè)有限小數(shù)。這是因?yàn)?10 的唯一素因子是 2 和 5。
      2. 小數(shù)位數(shù)的估算:
        假設(shè)我們有一個(gè)分?jǐn)?shù) \(\frac{a}{b}\),其中 (a) 和 (b) 是整數(shù),并且 (b) 僅包含 2 和 5 作為素因子。我們可以將 (b) 表示為 \((2^m \times 5^n)\),其中 (m) 和 (n) 是非負(fù)整數(shù)。
      3. 小數(shù)位數(shù)的上限:
        為了使分?jǐn)?shù) \(\frac{a}{b}\) 變成一個(gè)整數(shù),我們需要將其乘以 \((10^{\max(m, n)})\)。因此,有限小數(shù)的最大位數(shù)可以通過(guò)以下公式估算:
        \(a.precision + \lceil \frac{10 \cdot b.precision}{3} \rceil\)
        其中 (a.precision) 是分子 (a) 的位數(shù),(b.precision) 是分母 (b) 的位數(shù)。

      具體例子
      假設(shè)我們有兩個(gè)數(shù):
      ? this = 12345(精度為 5,因?yàn)橛?5 位數(shù)字)
      ? divisor = 678(精度為 3,因?yàn)橛?3 位數(shù)字)
      我們想知道 12345 除以 678 的商,如果是有限小數(shù),它的小數(shù)部分最多有多少位。根據(jù)公式:
      \(5 + \lceil \frac{10 \cdot 3}{3} \rceil = 5 + \lceil 10 \rceil = 5 + 10 = 15\)
      因此,12345 除以 678 的商,如果是有限小數(shù),小數(shù)部分最多有 15 位。

      額...是數(shù)論啊?那我走,打擾了,打擾了...

      為什么你應(yīng)該用字符串來(lái)構(gòu)造BigDecimal

      聰明的你應(yīng)該早就發(fā)現(xiàn)了,BigDecimal的構(gòu)造方法有很多個(gè)。應(yīng)該用哪個(gè)呢?很多人都知道應(yīng)該用字符串,可是為什么呢?
      因?yàn)椋?dāng)你不用字符串的時(shí)候,會(huì)用很多意想不到的驚喜。

          BigDecimal strnum = new BigDecimal("12.1234");
          System.out.println(String.format("precision:%s scale:%s", strnum.precision(),strnum.scale()));//輸出 precision:6 scale:4
      
          BigDecimal num = new BigDecimal(12.1234);
          System.out.println(String.format("precision:%s scale:%s", num.precision(),num.scale()));//輸出precision:50 scale:48
      

      下面一個(gè)刻度是48,這是什么鬼?

      再試試除法

          BigDecimal one = new BigDecimal("0.1");
      	BigDecimal eight = new BigDecimal("8");
          System.out.println(one.divide(eight));//輸出 0.0125
      
          BigDecimal _1 = new BigDecimal(0.1);
      	BigDecimal _8 = new BigDecimal(8);
          System.out.println(_1.divide(_8));//輸出 0.0125000000000000006938893903907228377647697925567626953125
      

      為什么會(huì)出現(xiàn)這種結(jié)果?這個(gè)原因眾所周知:

      二進(jìn)制(也稱(chēng)為基數(shù)2)不能精確表示某些十進(jìn)制數(shù),尤其是那些在十進(jìn)制中有有限小數(shù)位但在二進(jìn)制中需要無(wú)限小數(shù)位的數(shù)。例如,0.1 和 0.2 在二進(jìn)制中無(wú)法精確表示。
      這是因?yàn)槎M(jìn)制系統(tǒng)只能使用 0 和 1 來(lái)表示數(shù)值,而某些十進(jìn)制數(shù)在轉(zhuǎn)換為二進(jìn)制時(shí)會(huì)變成無(wú)限循環(huán)小數(shù)。例如:
      ? 0.1 在二進(jìn)制中表示為 0.00011001100110011...(無(wú)限循環(huán))
      ? 0.2 在二進(jìn)制中表示為 0.0011001100110011...(無(wú)限循環(huán))
      由于計(jì)算機(jī)的存儲(chǔ)空間有限,這些無(wú)限循環(huán)小數(shù)只能被截?cái)啵瑥亩鴮?dǎo)致精度損失。這就是為什么在使用浮點(diǎn)數(shù)進(jìn)行計(jì)算時(shí),可能會(huì)出現(xiàn)精度問(wèn)題。

      如果你看源碼你會(huì)發(fā)現(xiàn) public BigDecimal(double val) 和 public BigDecimal(String val)的實(shí)現(xiàn)完全不同。或許你也會(huì)想為什么呢?為什么要實(shí)現(xiàn)兩套不同的呢?你就直接這樣:

      public BigDecimal(double val) {
          this(String.valueOf(val));
      }
      

      不就完了?

      至于JDK團(tuán)隊(duì)實(shí)現(xiàn)兩套的原因是什么?你知道嗎?歡迎留言告訴我:)


      著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
      原文: https://wangxuan.me/tech/2024/07/16/the-precision-and-scale-of-BigDecimal.html

      posted on 2024-07-17 14:49  王君敕  閱讀(1105)  評(píng)論(2)    收藏  舉報(bào)

      主站蜘蛛池模板: 中国性欧美videofree精品| 麻豆人人妻人人妻人人片av| 综合偷自拍亚洲乱中文字幕| 国产精品亚洲mnbav网站| 亚洲午夜福利AV一区二区无码| 惠东县| 亚洲一区二区三区黄色片| 免费无码肉片在线观看| 久久99精品久久久久麻豆| 国产一精品一av一免费| 精品国精品国自产在国产| 国产极品美女高潮无套| 又爽又黄又无遮挡的激情视频| 人妻少妇精品视频三区二区| 国产又色又爽又黄的视频在线 | 日韩国产亚洲欧美成人图片| 国产不卡一区不卡二区| 亚洲国产成熟视频在线多多| 成人三级视频在线观看不卡| 国产精品尤物午夜福利| a男人的天堂久久a毛片| 久久亚洲中文字幕伊人久久大| 香蕉EEWW99国产精选免费| 亚洲深深色噜噜狠狠网站| 国语做受对白XXXXX在线| 亚洲av成人免费在线| 中文字幕人妻中出制服诱惑| 亚洲日韩一区二区| 欧美老熟妇乱子伦牲交视频| 亚洲国产精品综合色在线| 日本一卡二卡不卡视频查询| 波多野结衣av无码| 动漫av网站免费观看| 亚洲欧洲美洲无码精品va| 亚洲大尺度一区二区三区| 亚洲VA中文字幕无码久久不卡 | 免费无码高潮流白浆视频| 精品一区二区三区在线观看l| 手机看片日本在线观看视频| 岢岚县| 99精品国产成人一区二区|