1. 程式人生 > >Java中double型別精度丟失問題

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); 
目前瞭解的就這幾種方法,要是再想到新的解法會再來更新此文章。