<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后,計(jì)算結(jié)果一定精確?

          共 5072字,需瀏覽 11分鐘

           ·

          2021-12-26 01:14




          前言

          過年了,年終也領(lǐng)完了,這不打算出去面試一波,看看自己在市場中的價(jià)值,于是我簡單的做了波簡歷,然后去面試一波,結(jié)果誰知,第一個(gè)面試就差點(diǎn)碰壁了,面試官竟然問我BigDecimal這個(gè)類,可是我不慌,心中有料,內(nèi)心不慌,于是輕松拿下了一波高薪offer

          BigDecimal,這個(gè)類其實(shí)對于經(jīng)常接觸金融、電商、支付的猿猿來說不算陌生,我也還算是熟悉,我也經(jīng)常用,但是很多時(shí)候我們只知道他的用法,并不知道他還有隱藏的細(xì)節(jié)

          首先,這是java.math包中提供的一種可以用來進(jìn)行更高精度運(yùn)算的類型,相較于double、float這些類型來說,BigDecimal在和金額計(jì)算打交道應(yīng)該說有著天然的優(yōu)勢,這個(gè)大家也很熟悉了,接下來我們一起來分析下BigDecimal中的哪些注意事項(xiàng)

          1、BigDecimal不能使用equals方法做等值比較

          2、BigDecimal使用double初始化時(shí)存在精度風(fēng)險(xiǎn)


          問題一

          這個(gè)問題其實(shí)真的是很細(xì)節(jié)了,不知道大家有沒有注意到,在《阿里巴巴Java開發(fā)手冊》中其實(shí)也有注明

          不知道你們在比較BigDecimal的時(shí)候都是怎么使用的,但是千萬不要用==這種方式來使用哦,這個(gè)應(yīng)該不用多說吧,BigDecimal屬于對象,不是基本類型,不能用==來比較

          一般說到這里,大家就知道了,對象的話肯定使用equals來進(jìn)行比較咯,這樣就沒問題了,告訴你,用equals比較也有問題

          你個(gè)渣,我懷疑你在騙我,那你告訴我為何,還有怎么解決?

          我該如何比較呢,自定義個(gè)類,繼承BigDecimal,重寫equals,當(dāng)然可以。但是其實(shí)有更好的辦法,在BigDecimal內(nèi)部提供了compareTo方法買這個(gè)方法可以直接判斷兩個(gè)數(shù)字的值,相等則返回0

          知其然,也要知其所以然,我肯定會解釋清楚的嘞

          我們來看個(gè)例子:

            BigDecimal bigDecimal1 = new BigDecimal(1);
          BigDecimal bigDecimal2 = new BigDecimal(1);
          System.out.println(bigDecimal1.equals(bigDecimal2));
          BigDecimal bigDecimal3 = new BigDecimal(1);
          BigDecimal bigDecimal4 = new BigDecimal(1.0);
          System.out.println(bigDecimal3.equals(bigDecimal4));
          BigDecimal bigDecimal5 = new BigDecimal("1");
          BigDecimal bigDecimal6 = new BigDecimal("1.0");
          System.out.println(bigDecimal5.equals(bigDecimal6));

          以上代碼輸出結(jié)果:true ?true ?false

          有的時(shí)候結(jié)果是true,有的時(shí)候結(jié)果卻是false,很奇怪,為什么呢?我們來看下BigDecimal的equals的源碼:

          public boolean equals(Object x) {
          if (!(x instanceof BigDecimal))
          return false;
          BigDecimal xDec = (BigDecimal) x;
          if
          (x == this)
          return true;
          if
          (scale != xDec.scale)
          return false;
          long
          s = this.intCompact;
          long
          xs = xDec.intCompact;
          if
          (s != INFLATED) {
          if (xs == INFLATED)
          xs = compactValFor(xDec.intVal);
          return
          xs == s;
          } else if (xs != INFLATED)
          return xs == compactValFor(this.intVal);
          return this
          .inflated().equals(xDec.inflated());
          }

          里面有一個(gè)scale標(biāo)度的比較,大概這就是為什么bigDecimal5和bigDeclmal6的比較結(jié)果是false的原因了。equals不僅會比較數(shù)值,還會比較這個(gè)標(biāo)度是否一樣


          標(biāo)度問題

          使用equals進(jìn)行比較的時(shí)候會比較數(shù)值大小和scale標(biāo)度問題,那為什么上面的bigDecimal1和2、bigDecimal3和4卻是相同的呢,難道是因?yàn)樗麄兊念愋褪莍nt、long,而bigDecimal5和6的類型是string,導(dǎo)致出現(xiàn)精度問題?

          BigDecimal有四種定義的類型,包括int、long、double、String四種,首先int和long類型都是整數(shù),標(biāo)度都是0。當(dāng)類型是double的時(shí)候,new Bigdecimal(double)? => new BigDecimale(0.1),實(shí)際傳入的是0.1000000000000000055511151231527827021181583404541015625,這個(gè)時(shí)候的標(biāo)度就是55,也就是小數(shù)點(diǎn)的個(gè)數(shù)。

          而對于 new bigDecimal(1.0)來說,實(shí)際上就是整數(shù),也就是不存在后綴,所以和整數(shù)的標(biāo)度大小是一樣的

          對于BigDecimal(String)來說,當(dāng)我們傳入一個(gè)字符串的時(shí)候,new BigDecimal("0.1")創(chuàng)建一個(gè)BigDecimal的時(shí)候,其實(shí)創(chuàng)建出來的值正好就是等于0.1的,那么他的標(biāo)題也就是1。如果使用的是new BigDecimal("0.10000"),此時(shí)標(biāo)度就是5,所以這也就是解釋了為什么最后的bigDecimal5和6的結(jié)果不一樣咯

          那如何解決呢?其實(shí)BigDecimal不僅提供了equals方法,還提供了一個(gè)compareTo()方法,這個(gè)方法其實(shí)就是只比較兩個(gè)數(shù)值的大小,感興趣的可以去研究研究


          問題二

          BigDecimal使用double初始化時(shí)存在精度風(fēng)險(xiǎn),那這是怎么一回事呢?其實(shí)在阿里開發(fā)手冊中也有這么一條建議,或者說是要求吧

          禁止使用構(gòu)造方法BigDecimal(double)的方式把double值轉(zhuǎn)化成BigDecimal對象

          我們知道,計(jì)算機(jī)是只認(rèn)識二進(jìn)制的,只認(rèn)識0和1,也就是說任何數(shù)據(jù)都會轉(zhuǎn)化成0和1存儲在計(jì)算機(jī)中,整數(shù)簡單,除二取余,逆序排列即可。而小數(shù)則不一定全部能轉(zhuǎn)化成二進(jìn)制,比如0.1,在轉(zhuǎn)換的過程中會出現(xiàn)循環(huán)的情況,所以這種事無法正確的存儲完整的數(shù)據(jù)的,計(jì)算機(jī)是無法精確的存儲這種數(shù)據(jù)的,所以計(jì)算機(jī)采用的是一定的精度來解決這個(gè)問題的,這就是IEEE 754(IEEE二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn))規(guī)范的主要思想。

          IEEE 754規(guī)定了多種表示浮點(diǎn)數(shù)值的方式,其中最常用的就是32位單精度浮點(diǎn)數(shù)和64位雙精度浮點(diǎn)數(shù)。

          在Java中,使用float和double分別用來表示單精度浮點(diǎn)數(shù)和雙精度浮點(diǎn)數(shù)。

          所謂精度不同,可以簡單的理解為保留有效位數(shù)不同。采用保留有效位數(shù)的方式近似的表示小數(shù)。


          BigDecimal如何精確計(jì)數(shù)?

          如果大家看過BigDecimal的源碼,其實(shí)可以發(fā)現(xiàn),實(shí)際上一個(gè)BigDecimal是通過一個(gè)"無標(biāo)度值"和一個(gè)"標(biāo)度"來表示一個(gè)數(shù)的。

          在BigDecimal中,標(biāo)度是通過scale字段來表示的。

          而無標(biāo)度值的表示比較復(fù)雜。當(dāng)unscaled value超過閾值(默認(rèn)為Long.MAX_VALUE)時(shí)采用intVal字段存儲unscaled value,intCompact字段存儲Long.MIN_VALUE,否則對unscaled value進(jìn)行壓縮存儲到long型的intCompact字段用于后續(xù)計(jì)算,intVal為空。

          涉及到的字段就是這幾個(gè):

          ?public?class?BigDecimal?extends?Number?implements?Comparable<BigDecimal>?{

          ????????private?final?BigInteger?intVal;

          ????????private?final?int?scale;?

          ????????private?final?transient?long?intCompact;

          ????}

          大家只需要知道BigDecimal主要是通過一個(gè)無標(biāo)度值和標(biāo)度來表示的就行了。

          那么標(biāo)度到底是什么呢?除了scale這個(gè)字段,在BigDecimal中還提供了scale()方法,用來返回這個(gè)BigDecimal的標(biāo)度。那么,scale到底表示的是什么,其實(shí)上面的注釋已經(jīng)說的很清楚了:

          如果scale為零或正值,則該值表示這個(gè)數(shù)字小數(shù)點(diǎn)右側(cè)的位數(shù)。如果scale為負(fù)數(shù),則該數(shù)字的真實(shí)值需要乘以10的該負(fù)數(shù)的絕對值的冪。例如,scale為-3,則這個(gè)數(shù)需要乘1000,即在末尾有3個(gè)0。

          如123.123,那么如果使用BigDecimal表示,那么他的無標(biāo)度值為123123,他的標(biāo)度為3。

          而二進(jìn)制無法表示的0.1,使用BigDecimal就可以表示了,及通過無標(biāo)度值1和標(biāo)度1來表示。

          我們都知道,想要?jiǎng)?chuàng)建一個(gè)對象,需要使用該類的構(gòu)造方法,在BigDecimal中一共有以下4個(gè)構(gòu)造方法:

          其中 BigDecimal(int)和BigDecimal(long) 比較簡單,因?yàn)槎际钦麛?shù),所以他們的標(biāo)度都是0。而BigDecimal(double) 和BigDecimal(String)的標(biāo)度就有很多學(xué)問了。


          BigDecimal(double)有什么問題


          BigDecimal中雖然提供了一個(gè)通過double創(chuàng)建BigDecimal的方法,但是這其中也挖下了一個(gè)坑

          我們知道,double表示的小數(shù)是不精確的,比如0.1這個(gè)數(shù)值,double只能表示他的近似值,所以當(dāng)我們使用new BigDecimal(0.1)的時(shí)候,實(shí)際上創(chuàng)建出來的數(shù)值并不是正好等于0.1的,而是一個(gè)近似值

          所以,如果我們在代碼中,使用BigDecimal(double) 來創(chuàng)建一個(gè)BigDecimal的話,那么是損失了精度的,這是極其嚴(yán)重的。

          那么,該如何創(chuàng)建一個(gè)精確的BigDecimal來表示小數(shù)呢,答案是使用String創(chuàng)建。

          而對于BigDecimal(String) ,當(dāng)我們使用new BigDecimal("0.1")創(chuàng)建一個(gè)BigDecimal 的時(shí)候,其實(shí)創(chuàng)建出來的值正好就是等于0.1的。

          那么他的標(biāo)度也就是1。

          但是需要注意的是,new BigDecimal("0.10000")和new BigDecimal("0.1")這兩個(gè)數(shù)的標(biāo)度分別是5和1,如果使用BigDecimal的equals方法比較,得到的結(jié)果是false,可以使用compareTo方法進(jìn)行比較

          那么,想要?jiǎng)?chuàng)建一個(gè)能精確的表示0.1的BigDecimal,請使用以下兩種方式:

          ????BigDecimal?recommend1?=?new?BigDecimal("0.1");

          ????BigDecimal?recommend2?=?BigDecimal.valueOf(0.1);


          ?


          求贊


          ?

          好了,以上就是全部內(nèi)容了,我是小仙人,你們的學(xué)習(xí)成長小伙伴

          ? ? ? ? ? ? ? ? ? ??

          我希望有一天能夠靠寫字養(yǎng)活自己,現(xiàn)在還在磨練,這個(gè)時(shí)間可能會有很多年,感謝你們做我最初的讀者和傳播者。請大家相信,只要給我一份愛,我終究會還你們一頁情的。

          再次感謝大家能夠讀到這里,我后面會持續(xù)的更新技術(shù)文章以及一些記錄生活的靈魂文章,如果覺得不錯(cuò)的,覺得【小仙】有點(diǎn)東西的話,求點(diǎn)贊、關(guān)注、分享三連

          哦,對了!后續(xù)的更新文章我都會及時(shí)放到這里,歡迎大家點(diǎn)擊觀看,都是干貨文章啊,建議收藏,以后隨時(shí)翻閱查看

          https://github.com/DayuMM2021/Java

          ?

          ?


          瀏覽 58
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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片免费视频网站野外 | 人妻摸一摸日日爽一爽,免费视频 |