【超詳細】解釋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
- GPIO口工作原理的超詳細解釋(附電路圖)
- tar命令的詳細解釋
- Java註解最全詳解(超級詳細)Java
- Java之BigDecimal詳解JavaDecimal
- Go Struct超詳細講解GoStruct
- Double BigDecimal 精度丟失總結Decimal
- docker 命令詳細解釋Docker
- top命令詳細解釋
- Servlet、HTTP詳細解釋!ServletHTTP
- Spring AOP全面詳解(超級詳細)Spring
- 矩陣分解--超詳細解讀矩陣
- Java之BigDecimal(存任意精度的浮點型)JavaDecimal
- JPS 命令詳細解釋
- Nginx location配置詳細解釋Nginx
- resin配置檔案的詳細解釋
- Elasticsearch BM25相關度演算法超詳細解釋Elasticsearch演算法
- shell程式設計-sed命令詳解(超詳細)程式設計
- SpringBoot註解最全詳解(整合超詳細版本)Spring Boot
- 又踩坑了!BigDecimal使用的5個坑!Decimal
- 《Kafka實戰》之生產者API使用(引數解釋超詳細)KafkaAPI
- HTML 超級連結詳細講解HTML
- vue的事件冒泡 最詳細解釋版本Vue事件
- mysql 5.7配置項最詳細的解釋MySql
- Application.Caller詳細解釋APP
- 超級詳細Tcpdump 的用法TCP
- A*演算法(超級詳細講解,附有舉例的詳細手寫步驟)演算法
- C++指標的概念解讀 超詳細C++指標
- 一看你就懂,超詳細 java 中的 ClassLoader 詳解Java
- JWT 超詳細分析JWT
- MySQL relay log 詳細引數解釋MySql
- ORACLE 跟蹤檔案詳細解釋Oracle
- mysqldump匯出引數詳細解釋MySql
- php.ini安全配置詳細解釋PHP
- 商業計算Java高精度計算BigDecimal類JavaDecimal
- BigDecimal為什麼能保證精度不丟失?Decimal
- GitHub和Git超超超詳細使用教程!Github