又踩坑了!BigDecimal使用的5個坑!
來源:撿田螺的小男孩
前言
大家好,我是田螺。
在日常開發中,BigDecimal類被廣泛用於精確的數值、金額的計算。但是在使用BigDecimal的過程中,存在以下這幾個坑,大家要注意一下哈~~
1.浮點數初始化的坑
反例:
BigDecimal problematic = new BigDecimal(0.1);
System.out.println("Problematic: " + problematic.toString());
//輸出 Problematic: 0.1000000000000000055511151231257827021181583404541015625
可以發現,直接使用new BigDecimal(double)
構造方法可能會得到一個看起來相當奇怪的結果。這是因為double
本身的精度問題會被帶入BigDecimal
中。為了避免這個問題,推薦使用String
引數的構造方法或者使用BigDecimal.valueOf
方法,例如new BigDecimal("0.1")
,這樣可以確保BigDecimal的精度。
由於計算機的資源是有限的,所以是沒辦法用二進位制精確的表示 0.1,只能用「近似值」來表示,就是在有限的精度情況下,最大化接近 0.1 的二進位制數,於是就會造成精度缺失的情況。
正例:
BigDecimal problematic1 = BigDecimal.valueOf(0.1);
System.out.println("Problematic: " + problematic1.toString());
BigDecimal problematic2 = new BigDecimal("0.1");
System.out.println("Problematic: " + problematic2.toString());
//輸出
Problematic: 0.1
Problematic: 0.1
2. 比較數值時使用compareTo()方法而非equals()
BigDecimal bd1 = new BigDecimal("0.10");
BigDecimal bd2 = new BigDecimal("0.1");
System.out.println(bd1.equals(bd2)); // 輸出: false
System.out.println(bd1.compareTo(bd2) == 0); // 輸出: true
BigDecimal的equals方法不僅比較數值,還會比較物件的scale(小數點後的位數),如果只想比較數值,而不考慮scale
,應該使用compareTo
方法。
3.做除法時,未指定精度可能異常
BigDecimal dividend = new BigDecimal("10");
BigDecimal divisor = new BigDecimal("3");
BigDecimal result = dividend.divide(divisor); // 未指定精度和舍入模式
System.out.println(result);
//輸出
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1693)
at com.example.demo.controller.Test.main(Test.java:26)
除法操作將10
除以3
,結果是無限迴圈小數3.3333...
,但由於未指定精度和舍入模式,會丟擲ArithmeticException
異常。
官方有給出解釋:
"If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations."
要使用BigDecimal
時,要記得指定精度,避免因為精度問題帶來的損失。
4. BigDecimal轉String,科學計數法展示問題
System.out.println( new BigDecimal("0.0000000000001").toString());
BigDecimal bigDecimal = new BigDecimal("1E+12");
System.out.println(bigDecimal.toString());
//輸出 1E-13
//輸出 1E+12
這是因為 toString()
某些場景下使用科學計數法展示。如果不想用任何計數法,可以使用 toPlainString()
System.out.println( new BigDecimal("0.0000000000001").toPlainString());
BigDecimal bigDecimal = new BigDecimal("1E+12");
System.out.println(bigDecimal.toPlainString());
//輸出
0.0000000000001
1000000000000
5. 注意使用setScale方法設定精度
BigDecimal number = new BigDecimal("123.4567");
BigDecimal roundedNumber = number.setScale(2, RoundingMode.HALF_UP);
//輸出
123.46
因為BigDecimal
的精度是無限的,因此一般在計算的時候,要注意設定精度幾位。
並且,RoundingMode.HALF_UP
是一種舍入模式,用於四捨五入,即當數字的一部分被捨去時,如果剩餘部分大於或等於0.5,則向上舍入。除了HALF_UP
之外,還有幾個常用的舍入模式:
UP
:遠離零方向舍入的舍入模式。總是在非零捨棄部分之前增加數字。DOWN
:接近零方向舍入的舍入模式。總是在非零捨棄部分之前減少數字。CEILING
:接近正無窮大的方向舍入的舍入模式。如果BigDecimal是正的,則舍入行為與UP相同;如果是負的,則舍入行為與DOWN相同。FLOOR
:接近負無窮大的方向舍入的舍入模式。如果BigDecimal是正的,則舍入行為與DOWN相同;如果是負的,則舍入行為與UP相同。HALF_DOWN
:向“最近鄰居”舍入,除非兩邊距離相等,此時向下舍入。HALF_EVEN
:向“最近鄰居”舍入,除非兩邊距離相等,此時向偶數舍入。這種模式也稱為“銀行家舍入法”,因為它減少了累計錯誤。
還有一個點,就是:使用setScale
方法實際上會產生一個全新的BigDecimal
例項,而不會更改原有物件。所以,當你用setScale
調整了數字精度後,別忘了把新生成的物件賦值回原來的變數。
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70024924/viewspace-3012315/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 踩了的Dockerfile的坑Docker
- 今天踩了一個基礎坑
- 踩了 Golang sync.Map 的一個坑Golang
- Go 最細節篇|記憶體回收又踩坑了Go記憶體
- Python 初學者容易踩的 5 個坑Python
- MQTT使用踩坑MQQT
- 【踩坑指南】執行緒池使用不當的五個坑執行緒
- 前端踩坑系列《六》——讓人又愛又恨的npm包前端NPM
- 時區的坑,別再踩了!
- 一不小心又踩feign的坑
- [踩坑] Go Modules 使用Go
- URLEncoder使用踩坑
- 踩了個DNS解析的坑,但我還是沒想通DNS
- VUE 使用中踩過的坑Vue
- React兩個bug踩坑React
- 一次痛苦又甜蜜的微信支付踩坑之旅
- 小程式踩坑填坑
- golang的踩坑Golang
- HTML5 錄音的踩坑之旅HTML
- HTML5中Audio使用踩坑彙總HTML
- Golang 需要避免踩的 50 個坑Golang
- JavaScript 踩坑心得— 為了高速(上)JavaScript
- angular踩坑Angular
- 相容踩坑
- Flutter 踩坑Flutter
- vue踩坑Vue
- CDH踩坑
- THEOS踩坑。。。
- protodep踩坑
- 使用 Typescript 踩 react-redux 的坑TypeScriptReactRedux
- 那些年使用Hive踩過的坑Hive
- H5視訊活動踩坑H5
- GeoServer 踩過的坑Server
- vue中使用protobuf踩坑記Vue
- vue+iframe使用及踩坑Vue
- 使用phoenix踩的坑與設計思考
- jmeter:測試片段使用的踩坑點JMeter
- ## H5 canvas畫圖白板踩坑H5Canvas