首頁  >  文章  >  Java  >  java中float與double精度遺失的實例詳解

java中float與double精度遺失的實例詳解

零下一度
零下一度原創
2017-06-28 11:38:431922瀏覽

1.java中int,float,long,double取值範圍

[java] view plain copy

public class TestOutOfBound {  

public static void main( String[] args) {  

  

#System.out.println(Integer.MAX_VALUE-(-Integer.MAX_VALUE)); //記憶體溢位  

#System.out. println(Integer.MAX_VALUE); //2的31次方-1,10個數位,正的20億左右,用在錢上面不一定夠  

System.out.println(Integer.MIN_VALUE) ; //負的2的31次方  

System.out.println(Long.MAX_VALUE); //2的64次方-1,19個數位,很大了,可放心用在錢上方  

System.out.println(Long.MIN_VALUE); //負的2的64次方  

System.out.println(Float.MAX_VALUE); //2的128次方-1,38個數位,比long多了一倍,這個主要用來做簡單數學精確運算使用  

System.out.println(Float.MIN_VALUE); //2的-149次方

System.out.println(Double.MAX_VALUE); //2的1024次方-1,308個數位,是float數位的10倍,主要用來做複雜運算和天文運算  

# #System.out.println(Double.MIN_VALUE); //2的-1074次方  

  

}  

}  

2.float與double精度丟失問題

例子:

[java] view plain copy

舉例:double result = 1.0 - 0.9;  

  

這個結果不用說了吧,都知道了,0.09999999999999998  


為什麼會出現這個問題呢,就這是java和其它計算機語言都會出現的問題,下面我們分析一下為什麼會出現這個問題:
float和double類型主要是為了科學計算和工程計算而設計的。他們執行二元浮點運算,這是為了在廣泛的數字範圍上提供較為精確的快速近似計算而精心設計的。然而,它們並沒有提供完全精確的結果,所以我們不應該用於精確計算的場合。 float和double類型尤其不適合用於貨幣運算,因為要讓一個float或double精確的表示0.1或10的任何其他負數次方值是不可能的(其實道理很簡單,十進制系統中能不能準確表示出1/3呢?

浮點運算很少是精確的,只要是超過精度能表示的範圍就會產生誤差。往往產生誤差不是因為數的大小,而是因為數的精度。因此,產生的結果接近但不等於想要的結果。尤其在使用 float 和 double 作精確運算的時候要特別小心。

現在我們就詳細剖析一下浮點型運算為什麼會造成精確度遺失?



[java] view plain copy

首先我們要先搞清楚以下兩個問題:  

#  

   (1) 十進位整數如何轉換為二進位數  

  

          

         演算法演算法演算法。舉例,11表示成二進位數:  

  

#                                 5/2=2   餘   1  

#  

                     2/2=1           1/2=0   餘   1  

  

         11二進位表示為(從下往上):1011  

  

        這裡提一點:只要遇到除以後的結果為0了就結束了,大家想一想,所有的整數大家除以2是不是一定能夠最終得到0。換句話說,所有的整數轉變為二進制數的演算法會不會無限地循環下去呢?絕對不會,整數絕對可以用二進位精確表示 ,但小數就不一定了。  

  

    (2) 十進位小數如何轉換為二進位數  

  

##

         演算法是乘以2至沒有了小數。舉例,0.9表示成二進位數  

  

#                                0.8(1.8的小數部分)* 2=1.6    取整數部份 1  

  

          ,                         0.2*2=0.4   取整數部分 0  

  

                   0.4*2=0.8   取整數部分      0.8*2=1.6 取整數部分 1  

  

                   0.6*2=1.2    取整數部分 0  

#         .........      0.9二進位表示為(由上至下): 1100100100100..... .  

  

         以注意:上面的計算過程循環了,也就是說*2永遠不可能消滅小數部分,使演算法將無限下去。很顯然,小數的二進位表示有時是不可能精確的 。其實道理很簡單,十進制系統中能不能準確表示出1/3呢?同樣二進位系統也無法準確表示1/10。這也就解釋了為什麼浮點型減法出現了"減不盡"的精度丟失問題。

3.解決方法一:

#如果不介意自己記錄十進制的小數點,而且數值不大,那麼可以使用long ,int等基本類型,具體用int還是long要看涉及的數值範圍大小,缺點是要自己處理十進制小數點,最明顯的做法就是處理貨幣使用分來計算,而不用元(只涉及加減)。

如:

[java] view plain copy

#int resultInt = 10 - 9;    


#double result = (double)  

double result = (double) /最後時候自己控制小數點  

4.解決方法二:

使用BigDecmal,而且需要在建構參數使用String類型。

在《Effective Java》這本書中就給了一個解決方法。書中也指出,float和double只能用來做科學計算或是工程計算,在商業計算等精確計算中,我們要用java.math.BigDecimal。

    BigDecimal類別一個有4個方法,我們只關心對我們解決浮點型資料進行精確計算有用的方法,即

BigDecimal(double value) // 將double型資料轉換變成BigDecimal型數據

    思路很簡單,我們先透過BigDecimal(double value)方法,將double型數據轉換成BigDecimal數據,然後就可以正常進行精確計算了。等計算完畢後,我們可以對結果做一些處理,例如 對除不盡的結果可以進行四捨五入。最後,再把結果由BigDecimal型數據轉換回double型數據。

    這個想法很正確,但如果你仔細看看API裡關於BigDecimal的詳細說明,你就會知道,如果需要精確計算,我們不能直接用double,而非要用String來構造BigDecimal不可!所以,我們又開始關心BigDecimal類別的另一個方法,就是能夠幫助我們正確完成精確計算的 BigDecimal(String value)方法。

// BigDecimal(String value)能夠將String型資料轉換成BigDecimal型資料

    那麼問題來了,想像一下吧,如果我們要做一個浮點型數據的加法運算,需要先將兩個浮點數轉為String型數據,然後用BigDecimal(String value)建構成BigDecimal,之後要在其中一個上呼叫add方法,傳入另一個作為參數,然後把運算的結果(BigDecimal)再轉換為浮點數。如果每次做浮點型資料的計算都要如此,你能夠忍受這麼煩瑣的過程嗎?至少我不能。所以最好的方法,就是寫一個類,在類中完成這些繁瑣的轉換過程。這 樣,在我們需要進行浮點型資料計算的時候,只要呼叫這個類別就可以了。網路上已經有高手為我們提供了一個工具類Arith來完成這些轉換操作。它提供以下靜態方法,可以完成浮點型資料的加減乘除運算和對其結果進行四捨五入的操作:

public static double add(double v1,double v2)
public static double sub (double v1,double v2)
public static double mul(double v1,double v2)
public static double div(double v1,double v2)
public static double div(double v1,double v2,int scale)
public static double round(double v,int scale)

#下面會附上Arith的原始碼,大家只要把它編譯保存好,要進行浮點數計算的時候,在你的在原始程式中導入Arith類別就可以使用以上靜態方法來進行浮點數的精確計算了。

附錄:Arith原始碼

[java] view plain copy

import java.math.BigDecimal;  

  

C

#/** 

* 由於Java的簡單型別無法精確的對浮點數運算,這個工具類別提供精 

* 確的浮點數運算,包括加減乘除和四捨五入。 

#*/  

  

public class Arith{  

    //預設除法運算精確度  ## DEF_DIV_SCALE = 10;  

    //此類別無法為實例化  

#    //這個類別中 

     * 提供精確的加法運算。 

     * @param v1 被加數 

     * @param v2 加數 

 */  

    public static double add(double v1,double v2){  

              BigDecimal b2 = new BigDecimal(Double.toString(v2));  

        return b1.add(b2).doubleValue();  

##> */  

    public static double sub(double v1,double v2){  

卷      BigDecimal b2 = new BigDecimal(Double.toString(v2));  

#        return b1.subtract(b2).doubleValue();  

## 

##    public static double mul(double v1,double v2){  

        Big    BigDecimal b2 = new BigDecimal(Double .toString(v2));  

#

        return b1.multiply(b2).doubleValue();  

    }  

#  

    /** 

     * 提供(相對)精確的除法運算,當發生除不盡的情況時,精確到 

     * 小數點以後10位元,以後的數字則為四捨五入。 

     * @param v1 被除數 

     * @param v2 除數 

 */  

#    public static double #        返回div(v1,v2,DEF_DIV_SCALE);  

    }  

#  

    /** 

     * 提供(相對)精確的除法運算。當發生除不盡的情況時,由scale參數指 

     * 定精確度,以後的數字四捨五入。 

     * @param v1 被除數 

     * @param v2 除數 

o   

     * @return 兩個參數的商 

     */  

#    public static double

#        if(scale            拋出新的IllegalArgumentEx整數或零”);  

        }  

#        BigDecimal b1 = new BigDecimal(Double.toString(v1));  

        BigDecimal b2 = new BigDecimal(Double.toString(v2));  

        return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();  

    }  

#    /** 

     * 提供精確的小數位元四捨五入處理。 

     * @param v 需要四捨五入的數字 

     * @param scale 小數點後保留幾位五 */  

    public static double round( #        if(scale            拋出new Illegal ”);  

        }  

#        BigDecimal b = 新 BigDecimal(Double.toString(v));  

        BigDecimal one = new BigDecimal("1");  

        return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();  

    }  

};  

以上是java中float與double精度遺失的實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn