為什么要有bigdecimal ,他是干什么的
float和double類型的主要設計目標是為了科學計算和工程計算。他們執行二進制浮點運算,這是為了在廣域數值范圍上提供較為精確的快速近似計算而精心設計的。然而,它們沒有提供完全精確的結果,所以不應該被用于要求精確結果的場合。但是,商業計算往往要求結果精確,這時候就要使用bigdecimal啦。
什么是bigdecimal
bigdecimal 由任意精度的整數非標度值 和32 位的整數標度 (scale) 組成。如果為零或正數,則標度是小數點后的位數。如果為負數,則將該數的非標度值乘以 10 的負scale 次冪。因此,bigdecimal表示的數值是(unscaledvalue × 10-scale)。
本文將給大家詳細介紹關于java中bigdecimal精度和相等比較的坑,下面話不多說了,來一起看看詳細的介紹吧
先想一下,創建bigdecimal對象的時候一般是怎么創建的?
- new一個,傳進去值
- bigdecimal.valueof方法,傳進去值
作為一個數字類型,經常有的操作是比較大小,有一種情況是比較是否相等。用equal方法還是compareto方法?這里就是一個大坑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//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方法返回的結果是什么?全都是true?no no no...
true
false
false
false
驚不驚喜,意不意外?原因是什么呢?看一下bigdecimal的equals方法的實現:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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
的精度是多少呢?看下源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// 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返回的結果都是相等:
1
2
3
4
5
6
7
8
9
10
11
|
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)); |
輸出結果
0
0
0
0
由此聯想到的一個更大的坑是,如果將bigdecimal的值作為hashmap的key,因為精度的問題,相同的值就可能出現hashcode值不同并且equals方法返回false,導致put和get就很可能會出現相同的值但是存取了不同的value。
再想一想,小數類型在計算機中本來就不能精確存儲,再把其作為hashmap的key就相當不靠譜了,以后還是少用。
另外需要注意的一點是,寫代碼調別人寫的方法時,最好是點進去看一下實現。再小再常用的方法,都可能埋著大坑
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://www.cnblogs.com/z941030/p/9639717.html