1. 程式人生 > >java中比較float和double的大小

java中比較float和double的大小

最近在專案中碰到了一個業務邏輯計算,程式碼如下(示例程式碼)

double val1 = ...;

double val2 = ...,

double dif = ...,

if (Math.abs(val1 - val2-dif) == 0){

  //do things

}

結果發現有一組資料:61.5,60.4,1.1無法達到正確的結果.有經驗的開發人員一眼就可以發現問題所在,也知道應該採用如下的方式修改程式碼(產品模式下要進行程式碼的抽取和封裝):

double exp = 10E-10;

if (Math.abs(val1 - val2-dif)>-1*exp && Math.abs(val1 - val2-dif)<exp){

 //do things

}

那為什麼上面程式碼中"Math.abs(val1 - val2-dif) == 0"的值為什麼會是false呢?這就引申到java的一個基礎問題,即java中浮點數的儲存機制.

Java 中的浮點數分為單精度和雙精度數,也就是float和double.

float在記憶體中跟int一樣,佔4個位元組,32 bit.

第1個bit表示符號,0表示正數,1表示負數,這個很好理解,不用多管.

第2-9個bit表示指數,一共8位(可以表示0-255),這裡的底數是2,為了同時表示正數和負數,這裡要減去127的偏移量.這樣的話範圍就是(-127到128),

另外全0和全1作為特殊處理,所以直接表示-126到127.

剩下的23位表示小數部分,這裡23位表示了24位的數字,因為有一個預設的前導1(只有二進位制才有這個特性).

     最後結果是:(-1)^(sign) * 1.f * 2^(exponent)

     這裡:sign是符號位,f是23bit的小數部分,exponent是指數部分,最後表示範圍是(因為正負數是對稱的,這裡只關心正數)

    2^(-126) ~~ 2(1-2^(-24)) * 2^127

    這個還不是float的取值範圍,因為標準中還規定了非規格化表示法,另外還有一些特殊規定.

非規格化表示:

    當指數部分全0而且小數部分不全0時表示的是非規格化的浮點數,因為這裡預設沒有前導1,而是0.

    取值位0.f * 2^(-126),表示範圍位 2^(-149)~~ (1-2^(-23)) * 2^(-126) 這裡沒有考慮符號.這裡為什麼是-126而不是-127? 如果是-127的話,那麼最大表示為

2^(-127)-2^(-149),很顯然2^(-127) ~~2^(-126) 就沒法表示了.

其他特殊表示

    1.當指數部分和小數部分全為0時,表示0值,有+0和-0之分(符號位決定),0x00000000表示正0,0x80000000表示負0.

    2.指數部分全1,小數部分全0時,表示無窮大,有正無窮和負無窮,0x7f800000表示正無窮,0xff800000表示負無窮.

    3.指數部分全1,小數部分不全0時,表示NaN,分為QNaN和SNaN,Java中都是NaN.

結論:

    可以看出浮點數的取值範圍是:2^(-149)~~(2-2^(-23))*2^127,也就是Float.MIN_VALUE和Float.MAX_VALUE.

References: