解讀IEEE標準754:浮點數表示

1、背景
  在IEEE標準754以前,業界並無一個統一的浮點數標準,相反,不少計算機制造商都設計本身的浮點數規則,以及運算細節。那時,實現的速度和簡易性比數字的精確性更受重視。
  直到1985年Intel打算爲其的8086微處理器引進一種浮點數協處理器的時候,聰明地意識到,做爲設計芯片者的電子工程師和固體物理學家們,也許並不能經過數值分析來選擇最合理的浮點數二進制格式。因而Intel在請加州大學伯克利分校的 William Kahan教授──最優秀的數值分析家之一來爲8087 FPU設計浮點數格式; 而這個傢伙又找來兩個專家來協助他,因而就有了KCS組合(Kahn, Coonan, and Stone)。 他們共同完成了Intel的浮點數格式設計,並且完成地如此出色,以至於IEEE組織決定採用一個很是接近KCS的方案做爲IEEE的標準浮點格式。目前,幾乎全部計算機都支持該標準,大大改善了科學應用程序的可移植性。

2、表示形式
  從表面上看,浮點數也是一串0和1構成的位序列(bit sequence),並非三頭六臂的怪物,更不會咬人。然而IEEE標準從邏輯上用三元組{S,E,M}表示一個數N,以下圖所示:

  N的實際值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), 甚至被稱做「小數」。

3、浮點數格式

  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雖然是23位或者52位,但它們只是表示小數點以後的二進制位數,也就是說,假定 M爲「010110011...」, 在二進制數值上實際上是「.010110011...」。而事實上,標準規定小數點左邊還有一個隱含位,這個隱含位一般,哦不,應該說絕大多數狀況下是1,那什麼狀況下是0呢?答案是N對應的n很是小的時候,好比小於 2^(-126)(32位單精度浮點數)。不要困惑怎麼計算出來的,看到後面你就會明白。總之,隱含位算是賺來了一位精度,因而M對應的m最後結果多是"m=1.010110011...」或者「m=0.010110011...」

4、計算e、m
  首先將提到令初學者頭疼的「規格化(normalized)」、「非規格化(denormalized)」。噢,其實並無這麼難的,跟我來!掌握它之後你會發現一切都很優雅,更美妙的是,規格化、非規格化自己的概念幾乎不怎麼重要。請牢記這句話: 規格化與否全看指數E!
  下面分三種狀況討論E,並分別計算e和m:
  
   一、規格化:當E的二進制位不全爲0,也不全爲1時,N爲規格化形式。此時e被解釋爲表示偏置(biased)形式的整數,e值計算公式以下圖所示:
  上圖中,|E|表示E的二進制序列表示的整數值,例如E爲"10000100",則|E|=132,e=132-127=5 。 k則表示E的位數,對單精度來講,k=8,則bias=127,對雙精度來講,k=11,則bias=1023。
  此時m的計算公式以下圖所示:
  
  標準規定此時小數點左側的隱含位爲1,那麼m=|1.M|。如M="101",則|1.M|=|1.101|=1.625,即 m=1.625

  二、非規格化:當E的二進制位所有爲0時,N爲非規格化形式。此時e,m的計算都很是簡單。
  注意,此時小數點左側的隱含位爲0。 爲何e會等於(1-bias)而不是(-bias),這主要是爲規格化數值、非規格化數值之間的平滑過渡設計的。後文咱們還會繼續討論。
  有了非規格化形式,咱們就能夠表示0了。把符號位S值1,其他全部位均置0後,咱們獲得了 -0.0; 同理,把全部位均置0,則獲得 +0.0。非規格化數還有其餘用途,好比表示很是接近0的小數,並且這些小數均勻地接近0,稱爲「逐漸下溢(gradually underflow)」屬性。
  
   三、特殊數值: 當E的二進制位全爲1時爲特殊數值。此時,若M的二進制位全爲0,則n表示無窮大,若S爲1則爲負無窮大,若S爲0則爲正無窮大; 若M的二進制位不全爲0時,表示NaN(Not a Number),表示這不是一個合法實數或無窮,或者該數未經初始化。
  
5、範例
  仔細研讀第四點後,再回憶一下 文章開頭計算n的公式,你應該寫出一個浮點編碼的實際值n了吧? 還不能嗎?不急,我先給你示範一下。咱們假定N是一個8位浮點數,其中,S佔1位,E佔4位,M佔3位。下面這張表羅列了N可能的正數形式,也包含了e、m等值,請你對照着這張表,重溫一下第四點,你會慢慢明白的。說實在的,這張表花了我很多功夫呢,幸虧TeX畫表格還算省事! 
  
  這張表裏頭有不少有趣的地方,我提醒一下:
  ★ 看 N 列,從上到下,二進制位表示是均勻遞增的,且增量都是一個最小二進制位。這不是偶然,正是巧妙設計的結果。觀察最大的非規格數,發現剛好就是M全爲1, E全爲0的狀況。因而咱們求出最大的非規格數爲:
  上面的公式中,h爲M的位數(如範例中爲3)。注意,公式等號右邊的第一項同時又是最小規格數的值(如範例中爲 8/512 );第二項則正是最小非規格數的值(如範例中爲1/512)即該浮點數能表示的最小正數。
  ★ 看 m 列,規格化數都是 1+ x 的形式,這個1正是隱含位1; 而非規格化數隱含位爲0, 因此沒有 "1+" 。
  ★ 看 n 列,非規格化數從上到下的增量都是 1/512, 且過渡到規格化數時,增量是平滑的,依舊是1/512。這正是非規格化數中e等於(1-bias)而不是(-bias)的緣故,也是巧妙設計的結果。 再繼續往下看,發現增量值逐漸增大。可見,浮點數的取值範圍不是均勻的。
  
6、實戰
  咱們用一小段彙編來測試一下,浮點數在內存中是如何表示的。測試環境: GentooLinux2006.0/GNU assembler version 2.16.1/GNU gdb 6.4/AMD XP1600+。 以下所示
代碼:
   
   
   
   
~/coding/assemble $ gdb float GNU gdb 6.4 Copyright 2005 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".f1 0x80490a4 <f1>: 5 (gdb) x/xw &f1 0x80490a4 <f1>: 0x40a00000 (gdb) x/f &f2 0x80490a8 <f2>: 0.100000001 (gdb) x/xw &f2 0x80490a8 <f2>: 0x3dcccccd (gdb)
  從上面的gdb命令結果能夠看出,浮點數5被表示爲 0x40a00000,二進制形式爲( 0 100 0000 1 010 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由此產生了。
  
7、未完成
  關於浮點數,還有不少東西(好比舍入偏差、除零異常等等)值得咱們深刻探討,但已經沒法在此繼續。這篇文章的目的僅在初步解釋IEEE標準754對浮點數的規定以及一些奇妙的地方。寫這篇文章花掉了我成天的時間,但也使我完全記住了之前讓我膽怯的東西──最重要的是,但願這篇文章對你們有點用處,也算我爲計算機科學基礎理論版以及Linuxsir.org作的一點貢獻。
  

參考書目:
①: Randall Hyde, The Art of Assembly Language, Vol.1, 4.2.1
②: Randal E. Bryant, David R. O’Hallaron, Computer Systems A Programmer’s Perspective (Beta Draft), PartⅠ, Chapt.Ⅱ, 2.4
③: Rechard Blum, Professional Assembly Language
相關文章
相關標籤/搜索