【超詳細】解釋BigDecimal精度的坑
看到這篇文章的,想必是遇到同樣的坑,那麼請慢慢的看下去
問題重現
BigDecimal b1 = new BigDecimal(0.1);
BigDecimal b2 = new BigDecimal(0.5);
System.out.println("b1="+b1+"\nb2="+b2);
---------------結果----------------------
b1=0.1000000000000000055511151231257827021181583404541015625
b2=0.5
為什麼b1的結果是後面有一大串數字,而b2卻是正確的呢?
分析
1. effective java 第48條解釋:
如果需要精確的答案,請避免使用float和double
float和double型別主要是為了科學計算和工程計算而設計,他們執行二進位制浮點運算,這是為了在廣泛的數值範圍上提供較為精確的快速近和計算而精心設計的,然而,他們並沒有提供完全精確的結果,所以不應該被用於精確的結果的場合
2.個人分析:
從effective java中,明確的說了,float和double是不精確的運算,他們只是為了廣泛的運算保證有一個相近的值
為什麼要使用二進位制計算double,float和double他們執行的二進位制浮點運算,因為使用二進位制計算效率要高,適用於日常運算,而二進位制也能給我提供一個相對準確的值
為什麼二進位制運算不能提供準確的值,其實不能說二進位制不能提供準確的值,而是二進位制不能準確的表示一個小數,就像十進位制不能準確的表示1/3,1/6等。
為什麼十進位制不能表示1/3 ,因為1/3=0.333333333333333。。。。。,我們始終不能說清楚這後面有多少個3,所以說十進位制表示不了1/3.
為什麼二進位制不能表示一個小數,舉例0.1換成二進位制,請看下面的運算
0.1 * 2 = 0.2 -----0
0.2 * 2 = 0.4 -----0
0.4 * 2 = 0.8 -----0
0.8 * 2 = 1.6 -----1
0.6 * 2 = 1.2 -----1
0.2 * 2 = 0.4 -----0
.
.
.
//你懂的 0.0001100110011001100110011001100110011001100110011001101
所以二進位制表示不了0.1,但是有的小數卻可以表示,就像十進位制能表示1/2一樣,請看0.5
0.5 * 2 = 1 -----1
//所以0.5用二進位制表示就是0.1
這也就解釋了,我們遇到的坑,new BigDecimal(0.1);返回一個錯誤的值,而 new BigDecimal(0.5);返回一個準確的值,從本質上講0.1,只要他的型別是double或者float,它本身就一個不準確的值,這其實跟BigDecimal無關。我們使用new BigDecimal(Double d);這個構造時,假如入參是0.1,這其中會將0.1轉成Double,而此時0.1的值就已經不準確了,比如以下程式碼
System.out.println(0.5*3);
System.out.println(0.1*3);
//這兩句程式碼,可能很清楚說明二進位制不能表示0.1,可以表示0.5
如果你還沒清楚這個坑的來歷,請執行這兩句程式碼,看看結果,帶著結果重新閱讀一遍我的分析
解決辦法
既然知道了坑的來歷,我們就只要避免這個坑就行了,這個坑說到底是double或者float造成的,那麼我們在構造BigDecimal的物件時,不用這兩個構造方法就行了,如:
//使用String構造
BigDecimal b1 = new BigDecimal("0.1");
//或者是:
BigDecimal b1 = BigDecimal.valueOf(0.1);
//點開valueOf的原始碼,可以看到在原始碼中也是用new BigDecimal(String);返回一個BigDecimal物件的
//原始碼如下:
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}
相關文章
- BigDecimal 詳解Decimal
- 當 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丟失,真坑!SQLServerJDBCDecimal
- Java之BigDecimal詳解JavaDecimal
- GPIO口工作原理的超詳細解釋(附電路圖)
- Double BigDecimal 精度丟失總結Decimal
- Java註解最全詳解(超級詳細)Java
- Servlet、HTTP詳細解釋!ServletHTTP
- JPS 命令詳細解釋
- Spring AOP全面詳解(超級詳細)Spring
- Go Struct超詳細講解GoStruct
- SpringBoot註解最全詳解(整合超詳細版本)Spring Boot
- Java之BigDecimal(存任意精度的浮點型)JavaDecimal
- Application.Caller詳細解釋APP
- 又踩坑了!BigDecimal使用的5個坑!Decimal
- Elasticsearch BM25相關度演算法超詳細解釋Elasticsearch演算法
- 矩陣分解--超詳細解讀矩陣
- vue的事件冒泡 最詳細解釋版本Vue事件
- mysql 5.7配置項最詳細的解釋MySql
- expdp/impdp 詳細引數解釋
- shell程式設計-sed命令詳解(超詳細)程式設計
- 《Kafka實戰》之生產者API使用(引數解釋超詳細)KafkaAPI
- C++指標的概念解讀 超詳細C++指標
- 超詳細的IIS部署
- 超詳細的ArrayList擴容過程(配合原始碼詳解)原始碼
- 一看你就懂,超詳細 java 中的 ClassLoader 詳解Java
- python協程詳細解釋以及例子Python
- MySQL relay log 詳細引數解釋MySql
- A*演算法(超級詳細講解,附有舉例的詳細手寫步驟)演算法
- 速度與精度的結合 - EfficientNet 詳解
- springboot常用註解大全(超詳細, 30個)Spring Boot
- BigDecimal為什麼能保證精度不丟失?Decimal
- JWT 超詳細分析JWT
- Element-ui(更新中表單最詳細的解釋)UI
- git常用命令stash詳細解釋。Git
- Java基礎之字串String詳細解釋Java字串
- MySQL explain執行計劃詳細解釋MySqlAI
- 超詳細講解頁面載入過程
- 超詳細的Ribbon原始碼解析原始碼