Java中double型別精度丟失問題
首先,之所以double浮點數型別中會出現精度不準確這個問題,是因為浮點數在計算機的儲存方法導致的,要想具體瞭解的話,可以檢視《計算機組成原理》這本書,裡面有比較詳盡的解答。
接下來由一道題來引出接下來要講的這個問題:
對任意給定的一個double型的數(小數點位數大於2),要求最後輸出的數,精確到小數點後兩位,採用四捨五入計算。
最開始的時候我是想到一個方法,是把整數部分和小數部分分開,在小數部分直接計算四捨五入,最後輸出的時候再重新加起來就好。下面是我當時寫的程式碼:
public static double TwoDecimalPlaces(double number){
int z = (int)number;
double x = number - z;
if((int)(x * 1000) % 10 >= 5){
return z + (double)((int)(x * 100) + 1)) / 100;
}
return z + (double)((int)(x * 100))) / 100;
}
這裡面首先我沒有考慮到整型溢位的問題,因為一大於Integer.MAX_VALUE或者小於Integer.MIN_VALUE,就會發生溢位。果然一個大數進去就出錯了(175464815.2154354)。再者,我發現裡面還有另外的錯誤,就是比如用2.675這個數的話,在到算小數部分的時候,x本應該是0.675的,但是最後卻是變成了0.6749999999999998了,但是我也是Debug才發現的這個問題。之後上網查了之後才發現,原來是double型別精度丟失造成的。
發現之後,我就用了BigDecimal來改,但是在這裡面又發現了一個問題,就是BigDecimal的建構函式中用Double的話,也是有可能會發生精度丟失的問題的。
public static double TwoDecimalPlaces(double number)
{
BigDecimal b = new BigDecimal(number);
return b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
}
上面程式碼很多測試用例都是能通過的,就是在只有三個小數位的數上精確出錯,比如還是這個數2.675,本來精確之後,答案應該是2.68的,但是我上面的程式碼執行之後答案卻是2.67。這就讓我很鬱悶了,到底是hi錯在哪裡呢?我在網上找了好久,最後還是在API中找到了答案。 API中是這樣解釋的: 注: 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) 方法。
public static double TwoDecimalPlaces(double number)
{
BigDecimal b = new BigDecimal(Double.toString(number));
return b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
}
最後,我還是跟API說的一樣,將double轉換為String之後在構造BigDecimal函式,這樣就應該沒錯了吧。(由於錯了太多次,不太敢相信這裡邊還有沒有其他的什麼精度丟失的情況。)
附:
其實,還有其他的方法來解決保留小數位數的,下面介紹幾種:
1、
java.text.DecimalFormat df = new java.text.DecimalFormat(”#.00″);
df.format(你要格式化的數字);
2、
double d = 3.1415926;
String result = String .format(”%.2f”);
3、(在Javabean中)
<bean:write name="entity" property="dkhAFSumPl" format="0.00" />
4、(其中,digits 顯示的數字位數
為格式化物件設定小數點後的顯示的最多位,顯示的最後位是舍入的)
double x=23.5455;
NumberFormat ddf1=NumberFormat.getNumberInstance() ;
ddf1.setMaximumFractionDigits(2);
String s= ddf1.format(x) ;
System.out.print(s);
目前瞭解的就這幾種方法,要是再想到新的解法會再來更新此文章。