官术网_书友最值得收藏!

建議3-1:了解IEEE 754浮點數

1.浮點數簡介

在計算機系統的發展過程中,業界曾經提出過許多種實數的表達方法,比較典型的有相對于浮點數(Floating Point Number)的定點數(Fixed Point Number)。在定點數表達法中,其小數點固定地位于實數所有數字中間的某個位置。例如,貨幣的表達就可以采用這種表達方式,如55.00或者00.55可以用于表達具有4位精度,小數點后有兩位的貨幣值。由于小數點位置固定,所以可以直接用4位數值來表達相應的數值。但我們不難發現,定點數表達法的缺點就在于其形式過于僵硬,固定的小數點位置決定了固定位數的整數部分和小數部分,不利于同時表達特別大的數或者特別小的數。因此,最終絕大多數現代的計算機系統都采納了所謂的浮點數表達法。

浮點數表達法采用了科學計數法來表達實數,即用一個有效數字Significand,有效數字也被稱為尾數(Mantissa)。。一個基數(Base)、一個指數(Exponent)以及一個表示正負的符號來表達實數。比如,666.66用十進制科學計數法可以表達為6.6666×102(其中,6.6666為有效數字,10為基數,2為指數)。浮點數利用指數達到了浮動小數點的效果,從而可以靈活地表達更大范圍的實數。

當然,對實數的浮點表示僅作如上的規定是不夠的,因為同一實數的浮點表示還不是唯一的。例如,上面例子中的666.66可以表達為0.66666×103、6.6666×102或者66.666×101三種方式。因為這種表達的多樣性,因此有必要對其加以規范化以達到統一表達的目標。規范的浮點數表達方式具有如下形式:

其中,d.dd…d為有效數字,β為基數,e為指數。

有效數字中數字的個數稱為精度,我們可以用p來表示,即可稱為p位有效數字精度。每個數字d介于0和基數β之間,包括0。更精確地說,±d0.d1d2…dp-1×βe表示以下數:

其中,對十進制的浮點數,即基數β等于10的浮點數而言,上面的表達式非常容易理解。如12.34,我們可以根據上面的表達式表達為:1×101+2×100+3×10-1+4×10-2,其規范浮點數表達為1.234×101

但對二進制來說,上面的表達式同樣可以簡單地表達。唯一不同之處在于:二進制的β等于2,而每個數字d只能在0和1之間取值。如二進制數1001.101,我們可以根據上面的表達式表達為:1×23+0×22+0×21+1×20+1×2-1+0×2-2+1×2-3,其規范浮點數表達為1.001101×23

現在,我們就可以這樣簡單地把二進制轉換為十進制,如二進制數1001.101轉換成十進制為:

由上面的等式,我們可以得出:向左移動二進制小數點一位相當于這個數除以2,而向右移動二進制小數點一位相當于這個數乘以2。如101.11=3/4,而10.111=7/8。除此之外,我們還可以得到這樣一個基本規律:一個十進制小數要能用浮點數精確地表示,最后一位必須是5(當然這是必要條件,并非充分條件)。規律推演如下面的示例所示:

我們也可以使用一段C程序來驗證,如代碼清單1-17所示。

代碼清單1-17 浮點數示例代碼


#include <stdio.h>
int main(void)
{
    float f1=34.6;
    float f2=34.5;
    float f3=34.0;
    printf("34.6-34.0=%f\n",f1-f3);
    printf("34.5-34.0=%f\n",f2-f3);
    return 0;
}

代碼清單1-17的運行結果如圖1-19所示。

圖1-19 代碼清單1-17的運行結果

之所以“34.6-34.0=0.599998”,產生這個誤差的原因是34.6無法精確地表達為相應的浮點數,而只能保存為經過舍入的近似值。而這個近似值與34.0之間的運算自然無法產生精確的結果。

上面闡述了二進制數轉換十進制數,如果你要將十進制數轉換成二進制數,則需要把整數部分和小數部分分別轉換。其中,整數部分除以2,取余數;小數部分乘以2,取整數位。如將13.125轉換成二進制數如下:

首先轉換整數部分(13),除以2,取余數,所得結果為1101。

其次轉換小數部分(0.125),乘以2,取整數位。轉換過程如下:

0.125×2=0.25 取整數位0

0.25×2=0.5 取整數位0

0.5×2=1 取整數位1

小數部分所得結果為001,即13.125=1101.001,用規范浮點數表達為1.101001×23

除此之外,與浮點表示法相關聯的其他兩個參數是“最大允許指數”和“最小允許指數”,即emax和emin。由于存在βp個可能的有效數字,以及emax-emin+1個可能的指數,因此浮點數可以按[log2(emax-emin+1)]+[log2(βp)]+1位編碼,其中最后的+1用于符號位。

2.浮點數表示法

直到20世紀80年代(即在沒有制定IEEE 754標準之前),業界還沒有一個統一的浮點數標準。相反,很多計算機制造商根據自己的需要來設計自己的浮點數表示規則,以及浮點數的執行運算細節。另外,他們常常并不太關注運算的精確性,而把實現的速度和簡易性看得比數字的精確性更重要,而這就給代碼的可移植性造成了重大的障礙。

直到1976年,Intel公司打算為其8086微處理器引進一種浮點數協處理器時,意識到作為芯片設計者的電子工程師和固體物理學家也許并不能通過數值分析來選擇最合理的浮點數二進制格式。于是,他們邀請加州大學伯克利分校的William Kahan教授(當時最優秀的數值分析家)來為8087浮點處理器(FPU)設計浮點數格式。而這時,William Kahan教授又找來兩個專家協助他,于是就有了KCS組合(Kahn、Coonan和Stone),并共同完成了Intel公司的浮點數格式設計。

由于Intel公司的KCS浮點數格式完成得如此出色,以致IEEE(Institute of Electrical and Electronics Engineers,電子電氣工程師協會)決定采用一個非常接近KCS的方案作為IEEE的標準浮點格式。于是,IEEE于1985年制訂了二進制浮點運算標準IEEE 754(IEEE Standard for Binary Floating-Point Arithmetic,ANSI/IEEE Std 754-1985),該標準限定指數的底為2,并于同年被美國引用為ANSI標準。目前,幾乎所有的計算機都支持IEEE 754標準,它大大地改善了科學應用程序的可移植性。

考慮到IBM System/370的影響,IEEE于1987年推出了與底數無關的二進制浮點運算標準IEEE 854,并于同年被美國引用為ANSI標準。1989年,國際標準組織IEC批準IEEE 754/854為國際標準IEC 559:1989。后來經修訂后,標準號改為IEC 60559。現在,幾乎所有的浮點處理器完全或基本支持IEC 60559。同時,C99的浮點運算也支持IEC 60559。

IEEE浮點數標準是從邏輯上用三元組{S,E,M}來表示一個數V的,即V=(-1)S×M×2E,如圖1-20所示。

圖1-20 IEEE浮點數表示形式

其中:

□ 符號位s(Sign)決定數是正數(s=0)還是負數(s=1),而對于數值0的符號位解釋則作為特殊情況處理。

□ 有效數字位M(Significand)是二進制小數,它的取值范圍為1~2-ε,或者為0~1-ε。它也被稱為尾數位(Mantissa)、系數位(Coefficient),甚至還被稱作“小數”。

□ 指數位E(Exponent)是2的冪(可能是負數),它的作用是對浮點數加權。

浮點數格式是一種數據結構,它規定了構成浮點數的各個字段、這些字段的布局及算術解釋。IEEE 754浮點數的數據位被劃分為三個段,從而對以上這些值進行編碼。其中:

□ 一個單獨的符號位s直接編碼符號s。

□ k位的指數段exp=ek-1…e1e0,編碼指數E。

□ n位的小數段frac=fn-1…f1f0,編碼有效數字M,但是被編碼的值也依賴于指數域的值是否等于0。

根據exp的值,被編碼的值可以分為如下幾種不同的情況。

(1)格式化值

當指數段exp的位模式既不全為0(即數值0),也不全為1(即單精度數值為255,以單精度數為例,8位的指數為可以表達0~255的255個指數值;雙精度數值為2047)的時候,就屬于這類情況。如圖1-21所示。

圖1-21 格式化值(單精度)

我們知道,指數可以為正數,也可以為負數。為了處理負指數的情況,實際的指數值按要求需要加上一個偏置(Bias)值作為保存在指數段中的值。因此,這種情況下的指數段被解釋為以偏置形式表示的有符號整數。即指數的值為:

E=e-Bias

其中,e是無符號數,其位表示為ek-1…e1e0,而Bias是一個等于2k-1-1(單精度是127,雙精度是1023)的偏置值。由此產生指數的取值范圍是:單精度為-126~+127,雙精度為-1022~+1023。

對小數段frac,可解釋為描述小數值f,其中0≤f<1,其二進制表示為0.fn-1…f1f0,也就是二進制小數點在最高有效位的左邊。有效數字定義為M=1+f。有時候,這種方式也叫作隱含的以1開頭的表示法,因為我們可以把M看成一個二進制表達式為1.fn-1fn-2…f0的數字。既然我們總是能夠調整指數E,使得有效數字M的范圍為1≤M<2(假設沒有溢出),那么這種表示方法是一種輕松獲得一個額外精度位的技巧。同時,由于第一位總是等于1,因此我們就不需要顯式地表示它。拿單精度數為例,按照上面所介紹的知識,實際上可以用23位長的有效數字來表達24位的有效數字。比如,對單精度數而言,二進制的1001.101(即十進制的9.625)可以表達為1.001101×23,所以實際保存在有效數字位中的值為:

00110100000000000000000

即去掉小數點左側的1,并用0在右側補齊。

根據上面所闡述的規則,下面以實數-9.625為例,來看看如何將其表達為單精度的浮點數格式。具體轉換步驟如下:

首先,需要將-9.625用二進制浮點數表達出來,然后變換為相應的浮點數格式。即-9.625的二進制為1001.101,用規范的浮點數表達應為1.001101×23

其次,因為-9.625是負數,所以符號段為1。而這里的指數為3,所以指數段為3+127=130,即二進制的10000010。有效數字省略掉小數點左側的1之后為001101,然后在右側用零補齊。因此所得的最終結果為:

最后,我們還可以將浮點數形式表示為十六進制的數據,如下所示:

即最終的十六進制結果為0xC11A0000。

(2)特殊數值

IEEE標準指定了以下特殊值:±0、反向規格化的數、±∞和NaN(如表1-8所示)。這些特殊值都是使用emax+1或emin-1的指數進行編碼的。

表1-8 IEEE 754特殊值

1)NaN。

當指數段exp全為1時,小數段為非零時,結果值就被稱為“NaN”(Not any Number),如圖1-22所示。

圖1-22 NaN(單精度)

一般情況下,我們將0/0或視為導致計算終止的不可恢復錯誤。但是,一些示例表明在這樣的情況下繼續進行計算是有意義的。這時候就可以通過引入特殊值NaN,并指定諸如0/0或之類的表達式計算來生成NaN而不是停止計算,從而避免此問題。表1-9中列出了一些可以導致NaN的情況。

表1-9 產生NaN的運算

2)無窮。

當指數段exp全為1,小數段全為0時,得到的值表示無窮。當s=0時是+∞,或者當s=1時是-∞。如圖1-23所示。

圖1-23 無窮大(單精度)

無窮用于表達計算中產生的上溢問題。比如兩個極大的數相乘時,盡管兩個操作數本身可以保存為浮點數,但其結果可能大到無法保存為浮點數,必須進行舍入操作。根據IEEE標準,此時不能將結果舍入為可以保存的最大浮點數(因為這個數可能與實際的結果相差太遠而毫無意義),而應將其舍入為無窮。對于結果為負數的情況也是如此,只不過此時會舍入為負無窮,也就是說符號域為1的無窮。

3)非格式化值。

當指數段exp全為0時,所表示的數就是非規格化形式,如圖1-24所示。

圖1-24 非格式化值(單精度)

在這種情況下,指數值E=1-Bias,而有效數字的值M=f,也就是說它是小數段的值,不包含隱含的開頭的1。

非規格化值有兩個用途:

第一,它提供了一種表示數值0的方法。因為規格化數必須得使有效數字M在范圍1≤M<2之中,即M≥1,因此它就不能表示0。實際上,+0.0的浮點表示的位模式為全0(即符號位是0,指數段全為0,而小數段也全為0),這就得到M=f=0。令人奇怪的是,當符號位為1,而其他段全為0時,就會得到值-0.0。根據IEEE的浮點格式來看,值+0.0和-0.0在某些方面是不同的。

第二,它表示那些非常接近于0.0的數。它們提供了一種屬性,稱為逐漸下溢出。其中,可能的數值分布均勻地接近于0.0。

下面的單精度浮點數就是一個非格式化的示例。

它被轉換成十進制表示大約等于1.4×10-45,實際上它就是單精度浮點數所能表達的最小非格式化數。以此類推,格式化值和非格式化值所能表達的非負數值范圍如表1-10所示。

表1-10 格式化值和非格式化值所能表達的非負數值范圍表

3.標準浮點格式

IEEE 754標準準確地定義了單精度和雙精度浮點格式,并為這兩種基本格式分別定義了擴展格式,如下所示:

□ 單精度浮點格式(32位)。

□ 雙精度浮點格式(64位)。

□ 擴展單精度浮點格式(≥43位,不常用)。

□ 擴展雙精度浮點格式(≥79位,一般情況下,Intel x86結構的計算機采用的是80位,而SPARC結構的計算機采用的是128位)。

其中,只有32位單精度浮點數是本標準強烈要求支持的,其他都是可選部分。下面就來對單精度浮點與雙精度浮點的存儲格式做一些簡要的闡述。

(1)單精度浮點格式

單精度浮點格式共32位,其中,s、exp和frac段分別為1位、k=8位和n=23位,如圖1-25所示。

圖1-25 單精度浮點數的存儲格式

其中,32位中的第0位存放小數段frac的最低有效位LSB(least significant bit),第22位存放小數段frac的最高有效位MSB(most significant bit);第23位存放指數段exp的最低有效位LSB,第30位存放指數段exp的最高有效位MSB;最高位,即第31位存放符號s。例如,單精度數8.25的存儲方式如圖1-26所示。

(2)雙精度浮點格式

雙精度浮點格式共64位,其中,s、exp和frac段分別為1位、k=11位和n=52位,如圖1-27所示。

圖1-26 8.25的存儲方式(單精度)

圖1-27 雙精度浮點數的存儲格式

其中,frac[31:0]存放小數段的低32位(即第0位存放整個小數段的最低有效位LSB,第31位存放小數段低32位的最高有效位MSB);frac[51:32]存放小數段的高20位(即第32位存放高20位的最低有效位LSB,第51位存放整個小數段的最高有效位MSB);第52位存放指數段exp的最低有效位LSB,第62位存放指數段exp的最高有效位MSB;最高位,即第63位存放符號s。

在Intel x86結構的計算機中,數據存放采用的是小端法(Little Endian),故較低地址的32位的字中存放小數段的frac[31:0]位。而在SPARC結構的計算機中,因其數據存放采用的是大端法(Big Endian),故較高地址的32位字中存放小數段的frac[31:0]位。

前面主要討論了IEEE 754的單精度與雙精度浮點格式,表1-11對浮點數的相關參數進行了總結,有興趣的讀者可以根據此表對其他浮點格式進行深入解讀。

表1-11 浮點格式參數總結

4.舍入誤差

舍入誤差是指運算得到的近似值和精確值之間的差異。大家知道,由于計算機的字長有限,因此在進行數值計算的過程中,對計算得到的中間結果數據要使用相關的舍入規則來取近似值,而這導致計算結果產生誤差。

在浮點數的舍入問題上,IEEE浮點格式定義了4種不同的舍入方式,如表1-12所示。其中,默認的舍入方法是向偶數舍入,而其他三種可用于計算上界和下界。

表1-12 四種舍入方式

表1-13是4種舍入方式的應用舉例。這里需要特別說明的是,向偶數舍入(向最接近的值舍入)方式會試圖找到一個最接近的匹配值。因此,它將1.4舍入成1,將1.6舍入成2,而將1.5和2.5都舍入成2。

表1-13 4種舍入方式的取值示例演示

或許看了上面的內容你會問:為什么要采用向偶數舍入這樣的舍入策略,而不直接使用我們已經習慣的“四舍五入”呢?

其原因我們可以這樣來理解:在進行舍入的時候,最后一位數字從1到9,舍去的有1、2、3、4;它正好可以和進位的9、8、7、6相對應,而5卻被單獨留下。如果我們采用四舍五入每次都將5進位的話,在進行一些大量數據的統計時,就會累積比較大的偏差。而如果采用向偶數舍入的策略,在大多數情況下,5舍去還是進位概率是差不多的,統計時產生的偏差也就相應要小一些。

同樣,針對浮點數據,向偶數舍入方式只需要簡單地考慮最低有效數字是奇數還是偶數即可。例如,假設我們想將十進制數舍入到最接近的百分位。不管用哪種舍入方式,我們都將把1.2349999舍入到1.23,而將1.2350001舍入到1.24,因為它們不是在1.23和1.24的正中間。另一方面我們將把兩個數1.2350000和1.2450000都舍入到1.24,因為4是偶數。

由IEEE浮點格式定義的舍入方式可知,不論使用哪種舍入方式,都會產生舍入誤差。如果在一系列運算中的一步或幾步產生了舍入誤差,在某些情況下,這個誤差將會隨著運算次數的增加而積累得很大,最終會得出沒有意義的運算結果。因此,建議不要將浮點數用于精確計算。

當然,理論上增加數字位數可以減少可能會產生的舍入誤差。但是,位數是有限的,在表示無限浮點數時仍然會產生誤差。在用常規方法表示浮點數的情況下,這種誤差是不可避免的,但是可以通過設置警戒位來減小。

除此之外,IEEE 754還提出5種類型的浮點異常,即上溢、下溢、除以零、無效運算和不精確。其中,每類異常都有單獨的狀態標志。鑒于篇幅有限,本節就不再詳細介紹,有興趣的讀者可以參考IEEE 754標準文檔《IEEE Standard 754 for Binary Floating-Point Arithmetic》進行學習。

主站蜘蛛池模板: 江山市| 黄石市| 云阳县| 苗栗市| 东宁县| 福海县| 云阳县| 怀仁县| 米易县| 长治市| 赫章县| 句容市| 都匀市| 阿荣旗| 沾化县| 金堂县| 福泉市| 南京市| 兴国县| 深州市| 乌兰察布市| 志丹县| 鄂托克前旗| 林周县| 河津市| 巴彦县| 明星| 恩平市| 白玉县| 林芝县| 潮安县| 波密县| 蓝山县| 米易县| 宜丰县| 汝州市| 雷州市| 阳泉市| 大关县| 习水县| 浙江省|