關於Java float和double精度范圍大小

Java float和double精度范圍大小

要想理解float和double的取值范圍和計算精度,必須先瞭解小數是如何在計算機中存儲的:

舉個例子:78.375,是一個正小數。要在計算機中存儲這個數,需要把它表示為浮點數的格式,先執行二進制轉換:

一、小數的二進制轉換(浮點數)

78.375的整數部分:

小數部分:

所以,78.375的二進制形式就是1001110.011

然後,使用二進制科學記數法,有

註意,轉換後用二進制科學記數法表示的這個數,有底有指數有小數部分,這個就叫做浮點數

二、浮點數在計算機中的存儲

在計算機中,保存這個數使用的是浮點表示法,分為三大部分:

  • 第一部分用來存儲符號位(sign),用來區分正負數這裡是0,表示正數
  • 第二部分用來存儲指數(exponent),這裡的指數是十進制的6
  • 第三部分用來存儲小數(fraction),這裡的小數部分是001110011

需要註意的是,指數也有正負之分,後面再講。

如下圖所示(圖片來自維基百科):

比如float類型是32位,是單精度浮點表示法:

  • 符號位(sign)占用1位,用來表示正負數,
  • 指數位(exponent)占用8位,用來表示指數,
  • 小數位(fraction)占用23位,用來表示小數,不足位數補0。

而double類型是64位,是雙精度浮點表示法:

  • 符號位占用1位,指數位占用11位,小數位占用52位。

到這裡其實已經可以隱隱看出:

  • 指數位決定瞭大小范圍,因為指數位能表示的數越大則能表示的數越大嘛!
  • 而小數位決定瞭計算精度,因為小數位能表示的數越大,則能計算的精度越大咯!

可能還不夠明白,舉例子吧:

  • float的小數位隻有23位,即二進制的23位,能表示的最大的十進制數為2的23次方,即8388608,即十進制的7位,嚴格點,精度隻能百分百保證十進制的6位運算。
  • double的小數位有52位,對應十進制最大值為4 503 599 627 370 496,這個數有16位,所以計算精度隻能百分百保證十進制的15位運算。

三、指數位的偏移量與無符號表示

需要註意的是指數可能是負數,也有可能是正數,即指數是有符號整數,而有符號整數的計算是比無符號整數麻煩的。所以為瞭減少不必要的麻煩,在實際存儲指數的時候,需要把指數轉換成無符號整數。那麼怎麼轉換呢?

註意到float的指數部分是8位,則指數的取值范圍是 -126到+127,為瞭消除負數帶來的實際計算上的影響(比如比較大小,加減法等),可以在實際存儲的時候,給指數做一個簡單的映射,加上一個偏移量,比如float的指數偏移量為127,這樣就不會有負數出現瞭

比如

  • 指數如果是6,則實際存儲的是6+127=133,即把133轉換為二進制之後再存儲。
  • 指數如果是-3,則實際存儲的是-3+127=124,即把124轉換為二進制之後再存儲。

當我們需要計算實際代表的十進制數的時候,再把指數減去偏移量即可。

對應的double類型,存儲的時候指數偏移量是1023。

四、小結一下

所以用float類型來保存十進制小數78.375的話,需要先轉換成浮點數,得到符號位指數小數部分

這個例子前面已經分析過,所以:

符號位是0,

指數位是6+127=133,二進制表示為10 000 101,

小數部分是001110011,不足部分請自動補0。

連起來用float表示,加粗部分是指數位,最左邊是符號位0,代表正數:

0 10000101 001110011 00000 00000 0000

如果用double來保存。。。自己計算吧,太多0瞭。

float和double的范圍到底是多少

Java中float占4個字節,32bit。計算范圍公式為 ((-1)^S)* (2^(E-127))*(1.M) ,其中S占一位是符號位,E所占8bit是指數位,M占23位是尾數位。

這裡一開始(1.M)部分一開始我一直沒想明白為什麼前面是1,突然有一天腦子開竅瞭,科學計數法表示的時候小數點前面就必須是1,所以規格化的時候小數點前面是1。

E占8位,所以大小是0-255,但是為瞭表示小數,指數部分需要可以是小數,對半一分,所以最後是E-127,也就是說指數部分為-127-128。

尾數部分沒什麼好說的,范圍就是1-1.11……(23位全是1)

註意 :尾數這裡1.1111實際上是 十進制的1 + 二進制的0.1111, 什麼意思呢, 舉例說明會清楚一點:

1.1 —-> 1+1/2 = 1.5 = 2-1/2

1.11 —–> 1+1/2 +1/4 = 1.75 = 2-1/4

總結一下上面的

按道理最大值應該是(2^128)*(2-2^(-23))=2^129-2^105=6.81*10^38,但是一般書上說的都是3.40*10^38,那麼問題又來瞭,為什麼會大瞭2倍?

排除掉所有出書的人抄來抄去的行為導致所有的書都錯瞭這個因素,那麼剩下的隻能是上面某個地方出瞭問題。首先,回到上面那個我加粗的規格化上去(我個人覺得完全可以用一般情況來代替這個詞),仔細想想,假如所有的數都是上面那種規格化表示的時候:

第一:得到的數永遠是(1.M)乘以一個數,指數部分是不會為0的,那麼0怎麼表示?

第二:無窮大和無窮小,還有NAN(not a number)又是怎麼用這32bit表示出來的?我曾經想過的一個解釋是,計算機裡沒有那個數就表示NAN嘛。。。以前還真覺得這個好有道理來著。但是計算機這個東西你隻能當做一個工具,也就是說它不能無中生有,它隻能處理我們給它的東西,所有無窮大無窮小還有NAN在計算機裡肯定有一種表示方式。

所以,一定還有非規格化的表示,也就是所謂的特殊情況。

第一:當E是8個0的時候,此時就不是(1.M)而是(0.M)瞭,這個時候就可以表示出0瞭,當然還可以表示那些非常接近0的數。

第二:當E是8個1的時候,如果小數域全是0,表示的是無窮,其餘的表示NAN。

由上可知,指數部分為(0-127)和(255-127)的時候表示的是兩種特殊情況,所以E的范圍應該是【-126,127】。最後,得出的結論是規格化的浮點數的表示范圍是 正負(2^127)*(2-2^(-23))=2^128-2^104=3.40*10^38

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: