浮點數是我們在編程中常用的一個數據類型,不知道大家想過沒有,它為什麼叫做float呢?
還有,計算機對浮點數的內部表示方法IEEE 874到底是怎麼回事?
要徹底理解浮點數,需要從計算機的底層存儲開始。
假設有一個32 bit的計算機,需要你來設計一個支持存儲「小數」的方案,你會怎麼辦呢?
最簡單的辦法就是把這32位存儲分成若干部分, 例如三個部分
(1) 用1位來表達正負位, 0為正, 1為負。
(2) 再劃出8位來表示整數部分
(3) 剩下的23位表示小數部分。
就像這樣:
上圖表示的數值就是182.375,由於小數點固定在了第23位和第24位之間,這種方式可以稱為「定點數」。
很明顯,由於整數部分的長度比較短,所能表示的數據的範圍就比較小。 小數部分比較長, 所能表示的精度就比較高。
我們暫時把這種數據類型叫做fixed number A 。
如果想要表達更大範圍的數怎麼辦? 我們還可以定義一個新的數據類型: fixed number B。 讓整數部分擴大一些。
用23位表示整數,這範圍比8位大多了, 但是精度又會受到損失了,可見用這種定點數的表示法,範圍和精度是一對兒矛盾。
如果再定義fixed number C, fixed number D, 程式設計師簡直就不知道用哪個了, 並且實現他們之間的計算也很麻煩。
所以定點數並不是完美的解決方案。
怎麼解決定點數的「僵化」問題呢?
我們都知道科學記數法,例如368.79 用科學計數法表示就是 3.6879 * 10 ^2 。
其中3.6879就是尾數,10 是基數, 2 是指數。
浮點數就是利用指數達到了小數點「浮動」的效果。從而可以靈活地表達更大範圍內的數, 比如 :
3.6879 * 10 ^ 2 = 368.79
1.2345 * 10 ^ 3 = 1234.5
7.89 * 10 ^ 2 = 789
小數點的位置是不固定的。
不過對於同一個浮點數,也有很多表達方式, 368.79 可以表達為:
3.6879 * 10 ^ 2
0.36879 * 10 ^ 3
36.879 * 10 ^ 1
由於其多樣性, 很多計算機廠商都設計了自己的表示浮點數的規則,以及對浮點數運算的細節。 多樣的規則對於程序的可靠性和移植性都是不利的。
1976年, 一家叫Intel的公司要設計一個叫做8087的晶片, 這個晶片在8086處理器(這可是大名鼎鼎的晶片啊,計算機系的同學估計很熟悉吧)上增加了支持浮點數的功能。 他們請加州大學伯克利分校的William Kahan教授作為顧問,幫助設計8087晶片。
Intel 還支持Kahan教授加入IEEE資助的制定工業標準的委員會, 最終這個委員會採納的標準非常接近於Kahan為Intel設計的標準,這就是大名鼎鼎的 IEEE 754 標準。
(碼農翻身註:這段描述來自於《深入理解計算機系統》)
該標準在1985年發布,成為了各個計算機廠商都支持的規範,大大提高了程序的可移植性。
最新的標準是2008年發布的 IEEE 754-2008 。
William Kahan教授
這個標準中有單精度(32位)和雙精度(64位), 我們以32位為例來介紹一下,理解了32位,64位就不在話下了。
用科學記數法表示,應該是這樣的:
(+ or - ) 1.(mantissa) * 2 ^ exponent
注意:小數點前面是有個1的。
我們接下來用一個例子來計算一下, 把5.8 這個10進位小數轉換為IEEE 754表示的浮點數。
首先,5.8 = 1.45 * 2 ^ 2,可能有人問,這是怎麼算出來的? 很簡單:
5.8/2 => 2.9
2.9/2 => 1.45
接下來就可以把他表示成IEEE 754的形式:
(1) 可以看出 符號位 s = 0
(2) 指數(exponent) 是2 嗎?
No! 指數也有正負之分,我們既要能用8位二進位數字表示正數,又要能表示負數。
所以2的8次方這256個數字要區分開來使用: 從0到127 表示負數, 從128到255表示正數。
127 被稱為 Bias value (偏置值)
所以exponent = 127 + 2 = 129 , 把129用二進位表示就是10000001。
(註:如果原始的指數是 - 2 , 那exponent 就是 127- 2 = 125)
(3) 尾數mantissa 是0.45 , 需要轉化成二進位,怎麼做呢? 也很簡單,不斷地乘2,取結果的整數部分就行,詳細過程如下:
可以看出,出現無限循環了,我們取夠IEEE 754要求的23位就行了(0.01 1100 1100 1100 1100 1100 1.),可見浮點數是不精確的!
最終,我們把5.8變成了符合IEEE 754 規範的浮點數表示:
很簡單的,對吧?
敏銳的同學可能已經看出問題了,這個尾數總是1.mantissa的形式, 那用這種方式怎麼才能表示零呢?
這就留作一個小問題讓大家去探索吧, 你會發現非規格化,無窮大,NaN等有趣的東西。
(完)