Java中BigDecimal精度和相等比較的坑
為什么要有BigDecimal ,他是干什么的
float和double類型的主要設計目標是為了科學計算和工程計算。他們執(zhí)行二進制浮點運算,這是為了在廣域數(shù)值范圍上提供較為精確的快速近似計算而精心設計的。然而,它們沒有提供完全精確的結(jié)果,所以不應該被用于要求精確結(jié)果的場合。但是,商業(yè)計算往往要求結(jié)果精確,這時候就要使用BigDecimal啦。
什么是BigDecimal
BigDecimal 由任意精度的整數(shù)非標度值 和32 位的整數(shù)標度 (scale) 組成。如果為零或正數(shù),則標度是小數(shù)點后的位數(shù)。如果為負數(shù),則將該數(shù)的非標度值乘以 10 的負scale 次冪。因此,BigDecimal表示的數(shù)值是(unscaledValue × 10-scale)。
本文將給大家詳細介紹關(guān)于Java中BigDecimal精度和相等比較的坑,下面話不多說了,來一起看看詳細的介紹吧
先想一下,創(chuàng)建BigDecimal對象的時候一般是怎么創(chuàng)建的?
- new一個,傳進去值
- BigDecimal.valueOf方法,傳進去值
作為一個數(shù)字類型,經(jīng)常有的操作是比較大小,有一種情況是比較是否相等。用equal方法還是compareTo方法?這里就是一個大坑
//new 傳進去一個double BigDecimal newZero = new BigDecimal(0.0); System.out.println(BigDecimal.ZERO.equals(newZero)); //new 傳進去一個字符串 BigDecimal stringNewZero = new BigDecimal("0.0"); System.out.println(BigDecimal.ZERO.equals(stringNewZero)); //valueOf 傳進去一個double BigDecimal noScaleZero = BigDecimal.valueOf(0.0); System.out.println(BigDecimal.ZERO.equals(noScaleZero)); //valueOf 傳進去一個double,再手動設置精度為1 BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1); System.out.println(BigDecimal.ZERO.equals(scaleZero));
用于比較的值全都是0,猜一猜上面幾個equals方法返回的結(jié)果是什么?全都是true?no no no...
true
false
false
false
驚不驚喜,意不意外?原因是什么呢?看一下BigDecimal的equals方法的實現(xiàn):
public boolean equals(Object x) { //類型不同,直接返回false if (!(x instanceof BigDecimal)) return false; BigDecimal xDec = (BigDecimal) x; //同一個對象,直接返回true if (x == this) return true; //精度不同,直接返回false!! 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()); }
從前面三個簡單的判斷就可以看出來,debug跟一下就知道是上面equals方法有三個返回false,都是因為精度不同。那么BigDecimal.ZERO
的精度是多少呢?看下源碼:
// Cache of common small BigDecimal values. private static final BigDecimal zeroThroughTen[] = { new BigDecimal(BigInteger.ZERO, 0, 0, 1), new BigDecimal(BigInteger.ONE, 1, 0, 1), new BigDecimal(BigInteger.valueOf(2), 2, 0, 1), new BigDecimal(BigInteger.valueOf(3), 3, 0, 1), new BigDecimal(BigInteger.valueOf(4), 4, 0, 1), new BigDecimal(BigInteger.valueOf(5), 5, 0, 1), new BigDecimal(BigInteger.valueOf(6), 6, 0, 1), new BigDecimal(BigInteger.valueOf(7), 7, 0, 1), new BigDecimal(BigInteger.valueOf(8), 8, 0, 1), new BigDecimal(BigInteger.valueOf(9), 9, 0, 1), new BigDecimal(BigInteger.TEN, 10, 0, 2), }; /** * The value 0, with a scale of 0. * * @since 1.5 */ public static final BigDecimal ZERO = zeroThroughTen[0];
BigDecimal.ZERO
值為0,精度為0.
而上面幾種返回false的case,都是因為精度不同。精度不同的原因,則是BigDecimal對象初始化的方式不同,從源碼上看,前三種初始化的方式都不同。
所以說,BigDecimal比較大小,還是用compareTo方法比較靠譜,改為compareTo之后,上面四個case返回的結(jié)果都是相等:
BigDecimal newZero = new BigDecimal(0.0); System.out.println(BigDecimal.ZERO.compareTo(newZero)); BigDecimal stringNewZero = new BigDecimal("0.0"); System.out.println(BigDecimal.ZERO.compareTo(stringNewZero)); BigDecimal noScaleZero = BigDecimal.valueOf(0.0); System.out.println(BigDecimal.ZERO.compareTo(noScaleZero)); BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1); System.out.println(BigDecimal.ZERO.compareTo(scaleZero));
輸出結(jié)果
0
0
0
0
由此聯(lián)想到的一個更大的坑是,如果將BigDecimal的值作為HashMap的key,因為精度的問題,相同的值就可能出現(xiàn)hashCode值不同并且equals方法返回false,導致put和get就很可能會出現(xiàn)相同的值但是存取了不同的value。
再想一想,小數(shù)類型在計算機中本來就不能精確存儲,再把其作為HashMap的key就相當不靠譜了,以后還是少用。
另外需要注意的一點是,寫代碼調(diào)別人寫的方法時,最好是點進去看一下實現(xiàn)。再小再常用的方法,都可能埋著大坑
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
java通過ssh連接服務器執(zhí)行shell命令詳解及實例
這篇文章主要介紹了java通過ssh連接服務器執(zhí)行shell命令詳解及實例方法的相關(guān)資料2017-02-02解決springboot的aop切面不起作用問題(失效的排查)
這篇文章主要介紹了解決springboot的aop切面不起作用問題(失效的排查),具有很好的參考價值,希望對大家有所幫助。 一起跟隨小編過來看看吧2020-04-04使用MyEclipse 開發(fā)struts2框架實現(xiàn)登錄功能(結(jié)構(gòu)教程)
這篇文章主要介紹了使用MyEclipse 開發(fā)struts2框架實現(xiàn)登錄功能(結(jié)構(gòu)教程)的相關(guān)資料,需要的朋友可以參考下2016-03-03Java Swing JSlider滑塊的實現(xiàn)示例
這篇文章主要介紹了Java Swing JSlider滑塊的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12SpringBoot實現(xiàn)評論回復功能(數(shù)據(jù)庫設計)
這篇文章主要介紹了SpringBoot實現(xiàn)評論回復功能(數(shù)據(jù)庫設計),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04