解讀IEEE標準754:浮點數表示
一、背景 在IEEE標準754之前,業界并沒有一個統一的浮點數標準,相反,很多計算機制造商都設計自己的浮點數規則,以及運算細節。那時,實現的速度和簡易性比數字的精確性更受重視。 直到1985年Intel打算為其的8086微處理器引進一種浮點數協處理器的時候,聰明地意識到,作為設計芯片者的電子工程師和固體物理學家們,也許并不能通過數值分析來選擇最合理的浮點數二進制格式。于是Intel在請加州大學伯克利分校的 WilliamKahan教授──最優秀的數值分析家之一來為8087 FPU設計浮點數格式;而這個家伙又找來兩個專家來協助他,于是就有了KCS組合(Kahn, Coonan, andStone)。 他們共同完成了Intel的浮點數格式設計,而且完成地如此出色,以致于IEEE組織決定采用一個非常接近KCS的方案作為IEEE的標準浮點格式。目前,幾乎所有計算機都支持該標準,大大改善了科學應用程序的可移植性。 二、表示形式 從表面上看,浮點數也是一串0和1構成的位序列(bit sequence),并不是三頭六臂的怪物,更不會咬人。然而IEEE標準從邏輯上用三元組{S,E,M}表示一個數N,如下圖所示: ![]() ![]() 其中: ★ n,s,e,m分別為N,S,E,M對應的實際數值,而N,S,E,M僅僅是一串二進制位。 ★ S(sign)表示N的符號位。對應值s滿足:n>0時,s=0; n<0時,s=1。 ★ E(exponent)表示N的指數位,位于S和M之間的若干位。對應值e值也可正可負。 ★ M(mantissa)表示N的尾數位,恰好,它位于N末尾。M也叫有效數字位(sinificand)、系數位(coefficient), 甚至被稱作“小數”。 三、浮點數格式 IEEE標準754規定了三種浮點數格式:單精度、雙精度、擴展精度。前兩者正好對應C語言里頭的float、double或者FORTRAN里頭的real、double精度類型。限于篇幅,本文僅介紹單精度、雙精度浮點格式。 ★ 單精度:N共32位,其中S占1位,E占8位,M占23位。 ![]() ★ 雙精度:N共64位,其中S占1位,E占11位,M占52位。
此時m的計算公式如下圖所示: 2、非規格化:當E的二進制位全部為0時,N為非規格化形式。此時e,m的計算都非常簡單。 有了非規格化形式,我們就可以表示0了。把符號位S值1,其余所有位均置0后,我們得到了 -0.0; 同理,把所有位均置0,則得到+0.0。非規格化數還有其他用途,比如表示非常接近0的小數,而且這些小數均勻地接近0,稱為“逐漸下溢(graduallyunderflow)”屬性。 3、特殊數值: 當E的二進制位全為1時為特殊數值。此時,若M的二進制位全為0,則n表示無窮大,若S為1則為負無窮大,若S為0則為正無窮大; 若M的二進制位不全為0時,表示NaN(Not a Number),表示這不是一個合法實數或無窮,或者該數未經初始化。 五、范例 仔細研讀第四點后,再回憶一下文章開頭計算n的公式,你應該寫出一個浮點編碼的實際值n了吧?還不能嗎?不急,我先給你示范一下。我們假定N是一個8位浮點數,其中,S占1位,E占4位,M占3位。下面這張表羅列了N可能的正數形式,也包含了e、m等值,請你對照著這張表,重溫一下第四點,你會慢慢明白的。說實在的,這張表花了我不少功夫呢,幸好TeX畫表格還算省事! ★ 看 N 列,從上到下,二進制位表示是均勻遞增的,且增量都是一個最小二進制位。這不是偶然,正是巧妙設計的結果。觀察最大的非規格數,發現恰好就是M全為1, E全為0的情況。于是我們求出最大的非規格數為: ★ 看 m 列,規格化數都是 1+ x 的形式,這個1正是隱含位1; 而非規格化數隱含位為0, 所以沒有 "1+" 。 ★ 看 n 列,非規格化數從上到下的增量都是 1/512,且過渡到規格化數時,增量是平滑的,依舊是1/512。這正是非規格化數中e等于(1-bias)而不是(-bias)的緣故,也是巧妙設計的結果。再繼續往下看,發現增量值逐漸增大??梢姡↑c數的取值范圍不是均勻的。 六、實戰 我們用一小段匯編來測試一下,浮點數在內存中是如何表示的。測試環境: GentooLinux2006.0/GNU assembler version 2.16.1/GNU gdb 6.4/AMD XP1600+。 如下所示 代碼: ~/coding/assemble $ gdb float 從上面的gdb命令結果可以看出,浮點數5被表示為 0x40a00000,二進制形式為( 0100 0000 1010 0000 ... 0000 0000)。紅色數字為E,可以看出|E|=129>0, 則e=129-bias=129-127=2 ; 藍色數字為M,且|E|>0,說明是規格化數,則m=|1.M|=|1.01000..000|=1.25 ; 由n的計算公式可以求得 n=(-1)^0 *1.25 * 2^2 = 5, 結果被驗證了。 同樣,你也可以驗證一下十進制浮點數0.1的二進制形式是否正確,你會發現,0.1不能表示為有限個二進制位,因此在內存中的表示是舍入(rounding)以后的結果,即 0x3dcccccd, 十進制為0.100000001, 誤差0.000000001由此產生了。 |
posted on 2009-10-11 23:46 pear_li 閱讀(968) 評論(0) 編輯 收藏 引用 所屬分類: Algorithm