源地址: http://blog.csdn.net/niannian_315/article/details/24354251java
今天在用BigDecimal「出現費解」現象,之前雖然知道要避免用,但沒研究過。藉此機會,查證一下分享給你們參詳參詳。web
在Java中常常能夠用到double轉BigDecimal,也常常進行除法運算,可是如下用法須要特別當心了。ide
- package com.ccxe.number;
-
- import java.math.BigDecimal;
- import java.math.RoundingMode;
-
- public class Test {
-
- public static void main(String[] args) {
-
-
- System.out.println(new BigDecimal(2.00).subtract(new BigDecimal(Double
- .toString(1.10))));
-
- System.out.println(new BigDecimal(Double.toString(2.00))
- .subtract(new BigDecimal(1.10)));
-
-
- System.out.println(new BigDecimal("2.00").subtract(new BigDecimal(
- "1.10")));
-
-
-
-
-
- System.out.println(new BigDecimal("2.00").divide(
- new BigDecimal("1.10"), 5, RoundingMode.HALF_EVEN));
-
- }
-
- }
一下將解釋這一現象。this
正文:編碼
引言中的代碼樣例,指明瞭兩個問題:spa
1,第11行:執行的結果,竟然和18行不一樣;.net
2,第22行:除不盡,竟然拋出異常。orm
並且更重要的是,這兩個問題在編碼時隱蔽性很強~。=blog
先看第1個問題:ci
一看到減法結果很長,立馬想到1.10在二進制表達時,是不能準確表達的。隨後在JDK API中找到了答案。說法以下:
- public BigDecimal(double val)
-
- 將 double 轉換爲 BigDecimal,後者是 double 的二進制浮點值準確的十進制表示形式。返回
- 的BigDecimal 的標度是使 (10scale × val) 爲整數的最小值。
-
- 注:
- (1)此構造方法的結果有必定的不可預知性。有人可能認爲在 Java 中寫入new BigDecimal(0.1)
- 所建立的 BigDecimal 正好等於 0.1(非標度值 1,其標度爲 1),可是它實際上等於
- 0.1000000000000000055511151231257827021181583404541015625。這是由於 0.1 沒法準確地表
- 示爲double(或者說對於該狀況,不能表示爲任何有限長度的二進制小數)。這樣,傳入 到構
- 造方法的值不會正好等於 0.1(雖然表面上等於該值)。
- (2)另外一方面,String 構造方法是徹底可預知的:寫入 new BigDecimal("0.1") 將建立一個
- BigDecimal,它正好 等於預期的 0.1。所以,比較而言,一般建議優先使用 String 構造方法。
- (3)當 double 必須用做 BigDecimal 的源時,請注意,此構造方法提供了一個準確轉換;
- 它不提供與如下操做相同的結果:先使用 Double.toString(double) 方法,而後使用
- BigDecimal(String) 構造方法,將 double 轉換爲 String。要獲取該結果,請使用
- static valueOf(double) 方法。
-
- 參數:
- val - 要轉換爲 BigDecimal 的 double 值。
- 拋出:
- NumberFormatException - 若是 val 爲無窮大或 NaN。
由此,能夠看出,果真又是經典的「10進制沒法精確表達爲2進制」問題。
再看第二個問題:
從異常信息「
- public BigDecimal divide(BigDecimal divisor)
- 返回一個 BigDecimal,其值爲 (this / divisor),其首選標度爲 (this.scale()
- - divisor.scale());若是沒法表示準確的商值(由於它有無窮的十進制擴展),
- 則拋出 ArithmeticException。
-
- 參數:
- divisor - 此 BigDecimal 要相除的值。
- 返回:
- this / divisor
- 拋出:
- ArithmeticException - 若是準確的商值沒有無窮的十進制擴展
以上爲JDK API 1.5中的說法,1.6中,多了一個意思,建議優先考慮以下方法:
- public BigDecimal divide(BigDecimal divisor,
- int scale,
- RoundingMode roundingMode)
能夠避免上面所報異常。