Java 浮點數float和double類型的表示範圍和精度

轉自:http://blog.csdn.net/zq602316498/article/details/41148063?utm_source=tuicool&utm_medium=referral


隱約記得,浮點數判斷大小好像有陷阱,因爲底層的二進制數不能精確表示所有的小數。有時候會產生讓人覺得莫名其妙的事情。

如在Java中,  

        0.99999999f==1f //true 

        0.9f==1f //false

要明白這些,首先要搞清楚float和double在內存結構


1、內存結構

float和double的範圍是由指數的位數來決定的。
float的指數位有8位,而double的指數位有11位,分佈如下:

float類型的存儲方式

float:

        1bit(符號位) 8bits(指數位) 23bits(尾數位)                            

            double類型數據的存儲方式


double:
        1bit(符號位) 11bits(指數位) 52bits(尾數位)
於是,float的指數範圍爲 -128~+127 ,而double的指數範圍爲 -1024~+1023 ,並且指數位是按補碼的形式來劃分的。
其中,負指數決定了浮點數所能表達的絕對值最小的非零數;

而正指數決定了浮點數所能表達的絕對值最大的數,也即決定了浮點數的取值範圍。
float的範圍爲-2^128 ~ +2^127 ,也即-3.40E+38 ~ +3.40E+38 ;

double的範圍爲-2^1024 ~ +2^1023 ,也即-1.79E+308 ~ +1.79E+308 。


2. 精度

float 和 double 的精度是由尾數的位數來決定的。浮點數在內存中是按科學計數法來存儲的,其整數部分始終是一個隱含着的“1”,由於它是不變的,故不能對精度造成影響。

float:2^23 = 8388608,一共七位,由於最左爲1的一位省略了,這意味着最多能表示8位數: 2*8388608 = 16777216 。有8位有效數字,但絕對能保證的爲7位,也即float的精度爲7~8位有效數字
double:2^52 = 4503599627370496,一共16位,同理,double的精度爲16~17位

之所以不能用f1==f2來判斷兩個數相等,是因爲雖然f1和f2在可能是兩個不同的數字,但是受到浮點數表示精度的限制,有可能會錯誤的判斷兩個數相等!

我們可以用下面這段代碼檢驗一下:

    float f1 = 16777215f;
    for (int i = 0; i < 10; i++) {
        System.out.println(f1);
        f1++;
    }

對於小數來說,更容易會因爲精度而出錯誤。

    float f = 2.2f;
    double d = (double) f;
    System.out.println(d);
    f = 2.25f;
    d = (double) f;
    System.out.println(d);
輸出結果爲:

        2.200000047683716
        2.25

對於這種簡單數的輸出結果會是這樣,是簡直無法忍受的。

其實通過上面關於兩種存儲結果的介紹,我們已經大概能找到答案。首先我們看看2.25的單精度存儲方式,轉化爲2進制位便是10.01,整理爲1.001*2 很簡單 

於是我們可以寫出2.25的內存分佈: 
        符號位爲:0
        指數爲1,用補碼錶示 0000 0001,轉爲移碼就是1000 0001。
        尾數位爲0010 0000 0000 0000 0000 000

而2.25的雙精度表示爲:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,這樣2.25在進行強制轉換的時候,數值是不會變的,而我們再看看2.2呢,2.2用科學計數法表示應該爲:將十進制的小數轉換爲二進制的小數的方法爲將小數*2,取整數部分,所以0.282=0.4,所以二進制小數第一位爲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,這樣永遠也不可能乘到=1.0,得到的二進制是一個無限循環的排列 00110011001100110011... ,對於單精度數據來說,尾數只能表示24bit的精度,所以2.2的float存儲爲:

單精度數202的存儲方式

但是這樣存儲方式,換算成十進制的值,卻不會是2.2的,因爲十進制在轉換爲二進制的時候可能會不準確,如2.2,而double類型的數據也存在同樣的問題,所以在浮點數表示中會產生些許的誤差,在單精度轉換爲雙精度的時候,也會存在誤差的問題,如下面的代碼,輸出結果就不一樣:

    float f = 2.2f;
    double d = (double) f;
    System.out.println(f);
    System.out.println(d);
對於能夠用二進制表示的十進制數據,如2.25,這個誤差就會不存在,所以會出現上面比較奇怪的輸出結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章