sponsored links

面試官問我:你確定用了BigDecimal後,計算結果一定精確?


前言

過年了,年終獎也領完了,這不打算出去面試一波,看看自己在市場中的價值,於是我簡單地做了波簡歷,然後去面試一波,結果誰知,第一個面試就差點碰壁了,面試官竟然問我BigDecimal這個類,可是我不慌,心中有料,內心不慌,於是輕鬆拿下了一波高薪offer

BigDecimal,這個類其實對於經常接觸金融、電商、支付的猿猿來說不算陌生,我也還算是熟悉,我也經常用,但是很多時候我們只知道他的用法,並不知道他還有隱藏的細節

首先,這是java.math包中提供的一種可以用來進行更高精度運算的型別,相較於double、float這些型別來說,BigDecimal在和金額計算打交道應該說有著天然的優勢,這個大家也很熟悉了,接下來我們一起來分析下BigDecimal中的哪些注意事項

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

2、BigDecimal使用double初始化時存在精度風險

面試官問我:你確定用了BigDecimal後,計算結果一定精確?

問題一

這個問題其實真的是很細節了,不知道大家有沒有注意到,在《阿里巴巴Java開發手冊》中其實也有註明

面試官問我:你確定用了BigDecimal後,計算結果一定精確?

不知道你們在比較BigDecimal的時候都是怎麼使用的,但是千萬不要用==這種方式來使用哦,這個應該不用多說吧,BigDecimal屬於物件,不是基本型別,不能用==來比較

一般說到這裡,大家就知道了,物件的話肯定使用equals來進行比較咯,這樣就沒問題了,告訴你,用equals比較也有問題

你個渣,我懷疑你在騙我,那你告訴我為何,還有怎麼解決?

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

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

我們來看個例子:

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

以上程式碼輸出結果:true true false

有的時候結果是true,有的時候結果卻是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());
}

裡面有一個scale標度的比較,大概這就是為什麼bigDecimal5和bigDeclmal6的比較結果是false的原因了。equals不僅會比較數值,還會比較這個標度是否一樣

標度問題

使用equals進行比較的時候會比較數值大小和scale標度問題,那為什麼上面的bigDecimal1和2、bigDecimal3和4卻是相同的呢,難道是因為他們的型別是int、long,而bigDecimal5和6的型別是string,導致出現精度問題?

BigDecimal有四種定義的型別,包括int、long、double、String四種,首先int和long型別都是整數,標度都是0。當型別是double的時候,new Bigdecimal(double) => new BigDecimale(0.1),實際傳入的是0.1000000000000000055511151231527827021181583404541015625,這個時候的標度就是55,也就是小數點的個數。

而對於 new bigDecimal(1.0)來說,實際上就是整數,也就是不存在後綴,所以和整數的標度大小是一樣的

對於BigDecimal(String)來說,當我們傳入一個字串的時候,new BigDecimal("0.1")建立一個BigDecimal的時候,其實創建出來的值正好就是等於0.1的,那麼他的標題也就是1。如果使用的是new BigDecimal("0.10000"),此時標度就是5,所以這也就是解釋了為什麼最後的bigDecimal5和6的結果不一樣咯

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

面試官問我:你確定用了BigDecimal後,計算結果一定精確?

問題二

BigDecimal使用double初始化時存在精度風險,那這是怎麼一回事呢?其實在阿里開發手冊中也有這麼一條建議,或者說是要求吧

面試官問我:你確定用了BigDecimal後,計算結果一定精確?

禁止使用構造方法BigDecimal(double)的方式把double值轉化成BigDecimal物件

我們知道,計算機是隻認識二進位制的,只認識0和1,也就是說任何資料都會轉化成0和1儲存在計算機中,整數簡單,除二取餘,逆序排列即可。而小數則不一定全部能轉化成二進位制,比如0.1,在轉換的過程中會出現迴圈的情況,所以這種是無法正確的儲存完整的資料的,計算機是無法精確的儲存這種資料的,所以計算機採用的是一定的精度來解決這個問題的,這就是IEEE 754(IEEE二進位制浮點數算術標準)規範的主要思想。

IEEE 754規定了多種表示浮點數值的方式,其中最常用的就是32位單精度浮點數和64位雙精度浮點數。

在Java中,使用float和double分別用來表示單精度浮點數和雙精度浮點數。

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

BigDecimal如何精確計數?

如果大家看過BigDecimal的原始碼,其實可以發現,實際上一個BigDecimal是透過一個"無標度值"和一個"標度"來表示一個數的。

在BigDecimal中,標度是透過scale欄位來表示的。

而無標度值的表示比較複雜。當unscaled value超過閾值(預設為Long.MAX_VALUE)時採用intVal欄位儲存unscaled value,intCompact欄位儲存Long.MIN_VALUE,否則對unscaled value進行壓縮儲存到long型的intCompact欄位用於後續計算,intVal為空。

涉及到的欄位就是這幾個:

 public class BigDecimal extends Number implements Comparable<BigDecimal> {

        private final BigInteger intVal;

        private final int scale; 

        private final transient long intCompact;

    }

大家只需要知道BigDecimal主要是透過一個無標度值和標度來表示的就行了。

那麼標度到底是什麼呢?除了scale這個欄位,在BigDecimal中還提供了scale()方法,用來返回這個BigDecimal的標度。那麼,scale到底表示的是什麼,其實上面的註釋已經說得很清楚了:

如果scale為零或正值,則該值表示這個數字小數點右側的位數。如果scale為負數,則該數字的真實值需要乘以10的該負數的絕對值得冪。例如,scale為-3,則這個數需要乘1000,即在末尾有3個0。

如123.123,那麼如果使用BigDecimal表示,那麼他的無標度值為123123,他的標度為3。

而二進位制無法表示的0.1,使用BigDecimal就可以表示了,及透過無標度值1和標度1來表示。

我們都知道,想要建立一個物件,需要使用該類的構造方法,在BigDecimal中一共有以下4個構造方法:

其中 BigDecimal(int)和BigDecimal(long) 比較簡單,因為都是整數,所以他們的標度都是0。而BigDecimal(double) 和BigDecimal(String)的標度就有很多學問了。

BigDecimal(double)有什麼問題

BigDecimal中雖然提供了一個透過double建立BigDecimal的方法,但是這其中也挖下了一個坑

我們知道,double表示的小數是不精確的,比如0.1這個數值,double只能表示他的近似值,所以當我們使用new BigDecimal(0.1)的時候,實際上創建出來的數值並不是正好等於0.1的,而是一個近似值

所以,如果我們在程式碼中,使用BigDecimal(double) 來建立一個BigDecimal的話,那麼是損失了精度的,這是極其嚴重的。

那麼,該如何建立一個精確的BigDecimal來表示小數呢,答案是使用String建立。

而對於BigDecimal(String) ,當我們使用new BigDecimal("0.1")建立一個BigDecimal 的時候,其實創建出來的值正好就是等於0.1的。

那麼他的標度也就是1。

但是需要注意的是,new BigDecimal("0.10000")和new BigDecimal("0.1")這兩個數的標度分別是5和1,如果使用BigDecimal的equals方法比較,得到的結果是false,可以使用compareTo方法進行比較

那麼,想要建立一個能精確的表示0.1的BigDecimal,請使用以下兩種方式:

    BigDecimal recommend1 = new BigDecimal("0.1");

    BigDecimal recommend2 = BigDecimal.valueOf(0.1);

面試官問我:你確定用了BigDecimal後,計算結果一定精確?

面試官問我:你確定用了BigDecimal後,計算結果一定精確?

求贊

好了,以上就是全部內容了,我是小仙人,你們的學習成長小夥伴

面試官問我:你確定用了BigDecimal後,計算結果一定精確?

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

再次感謝大家能夠讀到這裡,我後面會持續的更新技術文章以及一些記錄生活的靈魂文章,如果覺得不錯的話,覺得【小仙】有點東西的話,求點贊、關注、分享三連

哦,對了!後續的更新文章我都會及時放到這裡,歡迎大家點選觀看,都是乾貨文章啊,建議收藏,以後隨時翻閱檢視

https://github.com/DayuMM2021/Java

分類: 科學
時間: 2021-12-23

相關文章

新研究找到腸癌潛在治療靶點
近日,上海交通大學醫學院附屬仁濟醫院消化科研究員洪潔和陳豪燕團隊發現,產腸毒素脆弱類桿菌(ETBF)處理大腸癌細胞後產生的外泌體中miR-149-3(微小RNA miR-149-3p)減少,促進了Th ...

英國《醫學快訊》:高鈉飲食被發現可抑制小鼠腫瘤的生長
[英國<醫學快訊>2021年9月20日文章]題:高鈉飲食被發現可抑制小鼠腫瘤的生長. 轉化健康科學與技術研究所研究人員證明,加鹽飲食可抑制小鼠癌性腫瘤的生長.發表在<科學進展> ...

英國《醫學快訊》:肥胖基因的發現可能促使產生預防體重增加的藥
[英國<醫學快訊>2021年9月20日文章]題:肥胖基因的發現可能促使產生預防體重增加的藥物. 美國弗吉尼亞大學科學家在研發肥胖症治療藥物上的進展振奮人心,發表在<公共科學圖書館·遺 ...

普林斯頓研究人員發現鼓勵人們接種COVID-19疫苗和戴口罩的新方法

普林斯頓研究人員發現鼓勵人們接種COVID-19疫苗和戴口罩的新方法
據外媒報道,全球已有超過2億人感染COVID-19,超400萬人死亡,儘管公共衛生官員.名人和有影響力的人做出了前所未有的努力,說服大家戴上口罩並儘快接種疫苗,但結果是好壞參半.現在,普林斯頓大學的兩 ...

澳大利亞國立大學研究人員研發“無能源浪費”原子級薄半導體

澳大利亞國立大學研究人員研發“無能源浪費”原子級薄半導體
澎湃新聞記者 邵文 澳大利亞國立大學(The Australian National University,簡稱ANU)的研究人員開發出一種只有原子般薄的半導體,厚度只有一張紙的10萬分之一,可用極其 ...

​研究人員用新的新技術,揭示了矽晶體從未被認識的特性

​研究人員用新的新技術,揭示了矽晶體從未被認識的特性
一組研究人員利用一項突破性的新技術揭示了以前從未重視過的矽晶體的特性,並發現了關於亞原子粒子和長期以來理論上的第五種力的新資訊. 美國國家標準與技術研究院的研究人員,領導的國際合作,使用一項開創性的新 ...

研究人員發現跳躍基因可能抹去了人類和類人猿的尾巴
從老鼠到猴子的哺乳動物都有尾巴.但是人類和我們的表親類人猿卻沒有尾巴.現在,研究人員可能已經發現了一個簡單的遺傳變化,一個流動的DNA片段躍入了一個新的染色體家族,並改變了類人猿如何製造一個關鍵的發育 ...

世界上最古老的藝術家?研究人員發現20萬年前兒童的手印

世界上最古老的藝術家?研究人員發現20萬年前兒童的手印
大約 20 萬年前,處於冰河時代的孩子們,在青藏高原海拔數千英尺的地方留下他們的手印和腳印,這些印記被歷史保留在了石灰岩中,提供了證明人類祖先居住在該地區的最早證據,並且可能代表了迄今為止發現的同類藝 ...

研究人員發現針對多種COVID-19變種的“超強抗體”

研究人員發現針對多種COVID-19變種的“超強抗體”
範德比爾特大學醫學中心開發的一項技術發現了一種針對SARS-CoV-2(導致COVID-19的病毒)多種變種的"超強"單克隆抗體,包括Delta變種.研究人員在<細胞報告&g ...

研究人員發現沸石分離鈣同位素違背了自然規律

研究人員發現沸石分離鈣同位素違背了自然規律
西北大學的研究人員分析了從東冰島邊緣收集的古代沸石標本,發現沸石以一種完全意想不到的方式分離鈣同位素.鈣以具有不同質量的多種同位素形式出現. 大多數礦物質優先加入較輕的鈣同位素.但是研究人員發現一些沸 ...

研究人員解開了通往量子未來的秘密之路

研究人員解開了通往量子未來的秘密之路
藝術家從金剛石中量子自旋缺陷的相互作用集合對流體動力學行為的說明.資料來源:諾曼·姚/伯克利實驗室 1998年,包括加州大學伯克利分校的Mark Kubinec在內的研究人員首次使用單個分子進行了簡單 ...

普渡大學研究人員研發出最白的油漆 對建築物冷卻效率強過家用空調

普渡大學研究人員研發出最白的油漆 對建築物冷卻效率強過家用空調
普渡大學的研究人員因研發出有史以來最白的油漆而登上了最新版的吉尼斯世界紀錄.儘管在標誌性的記錄簿中擁有一項記錄是多麼酷,但這並不是研究人員在著手進行該專案時的目標,研究人員想做的是幫助遏制全球變暖. ...

瑞士研究人員計算π至62.8萬億位,為何大家如此熱衷計算圓周率?

瑞士研究人員計算π至62.8萬億位,為何大家如此熱衷計算圓周率?
瑞士研究人員耗時108天計算出圓周率小數點後62.8萬億位,創下新的世界紀錄.他們使用計算機得出的計算結果,打敗此前的世界紀錄--小數點後50萬億位,計算速度也比之前的紀錄快了3.5倍.這是一個令人印 ...

研究人員開發Phoby增強現實應用:可幫助對抗蜘蛛恐懼症

研究人員開發Phoby增強現實應用:可幫助對抗蜘蛛恐懼症
據外媒報道,對於那些仍然與某種程度的蜘蛛恐懼症作鬥爭的人來說,可能會有一個新的應用程式來幫助他們.來自瑞士巴塞爾大學的研究人員開發了一個稱為Phoby的增強現實應用程式,旨在提供"暴露療法& ...

美國研究人員在阿拉斯加新發現了一座超級火山

美國研究人員在阿拉斯加新發現了一座超級火山
據美媒報道美國阿拉斯加火山觀測站最新的一項研究表明在美國阿拉斯加州阿留申群島中部的一群火山島是以前未被發現的一座超級火山火山口的一部分,由於這座超級火山火山口在海底因此很大程度上這座超級火山口都被海洋 ...

研究人員估算:前往火星的最佳出發時機,是太陽最活躍的極大期

研究人員估算:前往火星的最佳出發時機,是太陽最活躍的極大期
美國的宇宙天氣研究者發表了這樣的估算:在將來的火星載人探查中,"宇宙飛船從地球出發的最合適的時期是太陽活動最大最活躍的時期,加上往返的航行以及執行任務,最好在4年以內".在極大期間 ...

研究人員創造了一種利用鐳射烹飪3D列印食物的方法

研究人員創造了一種利用鐳射烹飪3D列印食物的方法
哥倫比亞大學的研究人員創造了一個可以使用鐳射烹飪食物的系統,並利用3D列印技術來組裝食物.創意機器實驗室的數字食品團隊的研究人員一直在努力建立一個自主的數字個人廚師.該團隊表示,印表機可以以毫米級的精 ...

日研究人員發明更輕更長新型機械臂
日本科學與技術高階研究所和東京大學的研究人員最近開發出一種機械肢體,取名為AugLimb.與其他大多數被安裝在上半身的可穿戴機械手臂相比,AugLimb摺疊後的體積小.重量輕,不影響佩戴者的日常活動, ...

美研究人員設計出最小的人造飛行器,可用於疾病追蹤
據國外媒體報道,研究人員設計出了一款"微型飛行器".研究人員稱,其目標是為小型電子系統新增有翼飛行裝置,這些功能將使該飛行器應用於汙染監測.人口監測或疾病追蹤.(鳳凰網科技)

研究人員正在開發下一代CasFET工藝,新技術將延長矽基電晶體壽命

研究人員正在開發下一代CasFET工藝,新技術將延長矽基電晶體壽命
三星在2020年宣佈攻克了3nm工藝節點的關鍵技術GAAFET全環繞柵極電晶體工藝,在今年3月份舉辦的IEEE國際積體電路會議上,三星介紹了該工藝的相關細節.這是目前FinFET鰭式場效應電晶體工藝的 ...