CSI-III:信息的表示與處理-數值陷阱(二)

     本篇繼續上一篇,進行浮點數的介紹,浮點數的表示並不像整型那樣簡單,其在計算機中的運算也會使用更多地時鐘週期。我們都知道計算機並不能絕對正確的表示浮點數,都是在允許的精度範圍內進行計算,這是由計算的信息表示方式(0和1)所決定的。所以,對於浮點數的誤差,我們更應該謹慎和小心,這樣才能夠在編寫的程序中避免可能的病態問題。

    談到浮點數,或許你能想到IEEE(讀作“Eye-Triple-Eee”)754標準。這個標準的制定是從1976年開始由Intel贊助的,在8087設計的同時,8087是一種爲8086處理器提供浮點支持的芯片。他們請Kahan作爲顧問,幫助設計未來處理器浮點標準。並支持Kahan加入IEEE委員會,後來IEEE最終制定的標準非常接近於Kahan爲Intel設計的標準。這樣IEEE754標準也就誕生了

1. 二進制小數

        在理解浮點數之前我們先看下含有小數值的二進制數字。首先先看下我們所熟悉的十進制是如何來表示小數的,十進制表示法所使用的表示形式爲:dmdm-1…d1d0.d-1d-2…d-n,其中每個十進制數di的取值爲0~9,這個表示的數值d爲:

                                                        

類似的,我們考慮形如bmbm-1…..b1b0.b-1b-2…b-n-1b-n的表示法,其中每個二進制數字bi的取值爲0或者1,這種表示方法表示的數b定義如下:

2.IEEE浮點表示

IEEE浮點標準用V=(-1)s×M×2E的形式來表示一個浮點數。

<a>符號s決定了這個數是負數(s=1)還是正數(s=0),對於數值0的符號作爲特殊情況處理。

<b>尾數 M是一個二進制小數,它的範圍是1~2-ε,或者是0~1-ε

<c>階碼E的作用是對浮點數加權,這個權重是2的E次冪(可能是負數)


將浮點數的位表示劃分爲三個字段,分別對這些值進行編碼:

<1>.一個單獨的符號位s直接編碼符號s.

<2>.K位的階碼字段exp=ek-1….e1e0編碼階碼E。

<3>.N位小數字段frac=fn-1….f1f0編碼尾數M,並且編碼出來的值也依賴於階碼字段的值是否等於0。


      在單精度浮點格式(float)中,s、exp和frac字段分別爲1位,k=8位和n=23位,得到一個32位的表示。在雙精度浮點格式(double)中,s、exp和frac的值分別爲1位、k=11位和n=52位,得到一個64位的表示。

 

給定了位表示,根據exp的值,被編碼的值可以分爲三種情況。

 

下面我們討論這三種情況:

(1).規格化

        當exp的位模式既不全爲0(數值0),也不全爲1(單精度數值爲255,雙精度數值爲2047)時,都屬於這類情況。在這種情況中,階碼字段被解釋爲以偏置形式表示的有符號整數。也就是說,階碼的值是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.我們可以將M看成一個二進制表達式爲1.fn-1fn-2…f0的數字。

(2)非規格化

      當階碼域爲全0時,所表示的數就是非規格化形式,在這種情況下,階碼值E=1-Bias,而尾數的值是M=f,也就是小數字段的值,不包含隱含的開頭的1.

這裏的階碼值爲1-Bias而不是簡單的-Bias,後面我再介紹這麼設置的原因。

(3).特殊值

       最後一類數值是當指階碼全爲1的時候出現的。當小數域全爲0時,得到的值表示無窮,當s=0時是+∞,或者當s=1時是-∞.當小數域爲非零時,結果值被稱爲”NaN”,即不是一個數。

可能針對上面的概念描述,你可能還不夠清楚浮點數具體是如何表示的,那麼下面我們看一個數字實例,在這個實例中我們假定8位的浮點格式,其中有k=4的階碼位和n=3的小數位。那麼偏置Bias=24-1-1=7,請看下圖:

      上圖只是非負值的浮點格式表示,從0自身開始,最靠近0的是給規格化數。這種格式的非規格數的E=1-7=-6,得到權2E=1/64.小數f的值得範圍是0,1/8,….7/8,從而得到數V的範圍是0~1/64*7/8=7/512.

      同時,從上圖中我們可以看到從最大的非規格化數7/512到最小的規格化數8/512的平滑轉變,這就是我們爲什麼在非規格化數將E定義爲1-Bias,而不是-Bias.

      當階碼增大時,我們就能夠得到更大的規格化的值。當取到最大的規格化數時,階碼E=7,尾數M=1+f=1+7/8=15/8,此時的數值爲V=27*15/8=1992/8=240。

      通過上面的介紹,你一定熟悉了IEEE754是如何進行浮點數的編碼的,下面我們再看一個使用32位浮點格式來對整數12345進行單精度的浮點數表示的例子:

我們知道了對於32位的單精度浮點數,其符號佔1位,階碼E佔8位,尾數M佔23位。整數12345的二進制表示爲[11 0000 0011 1001],通過將二進制小數點左移13位。我們得到這個數的規格化表示1.1 0000 0011 1001×213,我們丟棄開頭的1,並在末尾增加10個0,以構造小數位,得到的二進制表示爲[1 0000 0011 1001 0000 0000 00]。爲了構造階碼字段,我們用13加上偏置127,得到140,其二進制爲[10001100],最後加上符號位0,我們得到這個數的浮點數二進制 表示爲[010001100 1 0000 0011 1001 0000 0000 00]。我們觀察到整數值12345和單精度浮點值12345.0在位級表示上有下列關係:

我們看到,浮點數尾數部分在整數表示的最高有效位1之前就停止了(這個位就是隱含的開頭的位1),和浮點表示的小數部分的高位是匹配的。

3.舍入

     計算機的表示方法限制了浮點數的範圍和精度,浮點運算只能近似地表示實數運算。因此,對於值x,我們一般享用一種系統的方法,能夠找到”最接近的”匹配值x’,它可以用期望的浮點形式表示出來。這就是舍入運算。IEEE浮點格式定義了四種不同的舍入方式:

     向偶數舍入、向零舍入、向下舍入、向上舍入。

     向偶數舍入也稱爲向最接近的值舍入,是默認的方式,試圖找到一個最接近的匹配值。當值是兩個可能結果的之間數值時,其採用的方法是:將數字向上或者向下舍入,使得結果的最低有效數字是偶數。因此,這種方法將1.5和2.5美元都舍入成2美元。

    有什麼理由偏向取偶數呢?爲什麼不始終把位於兩個可表示的值中間的值都向上舍入呢?假設有這樣的場景:這組方法舍入一組數值,會在計算這些值的平均數中引入統計偏差。我們採用這種舍入得到的一組值得平均值將比這些數本身的平均值略高。相反,如果採用向下舍入,那麼得到的平均值就略低。而向偶數舍入在大多數現實情況中避免了這種統計偏差。在50%的時間裏,它將向下舍入,而在50%的時間裏,它將向上舍入。


4.浮點運算

     在浮點運算中,浮點加法不具有結合性。例如,表達式(3.14+1e10)-1e10求值得到0.0—因爲舍入,3.14會丟失。另一方面,表達式3.14+(1e10-1e10)得到值3.14.

     浮點乘法也遵循通常乘法所具有的許多屬性。當同時由於可能發生溢出,或者由於舍入而失去精度,它不具有結合性。例如,在單精度浮點情況下,表達式(1e20*1e20)*1e-20的值爲+∞,而1e20*(1e20*1e-20)將得出1e20.另外,浮點乘法在加法上不具備分配性。例如,在單精度浮點情況下,表達式1e20*(1e20-1e20)的值爲0.0,而1e20*1e20-1e20*1e20會得到NaN.


5.C語言的浮點數

     C語言提供了兩種不同的浮點數據類型:float和double。當int、float和double格式之間進行強制類型轉換時,程序改變數值和位模式的原則如下:

(1)從int 轉換成float,數字不會溢出,但是可能被舍入。

(2)從int 或者float轉換成double,因爲dobule有更大的範圍和精度,所以能夠保留精確的數值。

(3)從double轉換成float,因爲範圍要小一些,所以值可能溢出爲-∞或者+∞,另外,由於精度較小,它還可能被舍入。

(4)從float或者double轉換成int,值將會向零舍入。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章