關於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。
推薦閱讀:
- Java學習筆記:關於Java double類型相加問題
- C#中32位浮點數Float(Real)一步步按位Bit進行分析
- Java Float 保留小數位精度的實現
- 深入瞭解Java核心類庫–Math類
- 詳解為什麼指針被譽為C語言靈魂