定點數與浮點數

目前在研究CNN卷積神經網絡在硬件平臺上的並行優化工作,相關知識需要補一補


轉自:http://www.cnblogs.com/kevinq/p/4480563.html

參考:

1.http://www.cnblogs.com/cloudseawang/archive/2007/02/06/641652.html

2.http://www.cnblogs.com/chenwu128/archive/2012/10/07/2714120.html

簡介:

本文主要介紹了定點數和浮點數的概念,定點數和浮點數的加減運算(比如34.6f-34.0f),最後介紹了浮點數的特殊值

I.定點數

所謂定點格式,即約定機器中所有數據的小數點位置是固定不變的。通常將定點數據表示成純小數或純整數,爲了將數表示成純小數,通常把小數點固定在數值部分的最高位之前;而爲了將數表示成純整數,則把小數點固定在數值部分的最後面,如下圖所示:

 

圖中所標示的小數點在機器中是不表示出來的,而是事先約定在固定的位置。對於一臺計算機,一旦確定了小數點的位置,就不再改變。

假設用n位來表示一個定點數x=x0x1x2...x(n-1),其中x0用來表示數的符號位,通常放在最左位置,並用數值0和1分別表示正號和負號,其餘位數表示它的量值。如果定點數x表示純整數,則小數點位於最低位x(n-1)的右邊,數值範圍是0<=|x|<=2^(n-1)-1,且,例如1111表示-7;如果定點數x表示純小數,則小數點位於x0和x1之間,數值範圍是0<=|x|<=1-2^(-(n-1)),且,例如1111表示-0.875.

 II.定點數加減運算

不論操作數是正還是負,在做補碼加減法時,只需將符號位和數值部分一起參與運算,並且將符號位產生的進位丟掉即可。如:

short A=-9, B=-5;

cout<<A+B<<endl;  //-14

推導過程如下:

A的原碼爲:1000 0000 0000 1001,因此補碼爲:1111 1111 1111 0111

B的原碼爲:1000 0000 0000 0101,因此補碼位:1111 1111 1111 1011

A+B的補碼爲:1 1111 1111 1111 0010,將符號位產生的進位丟掉,因此最終結果爲:

1111 1111 1111 0010,結果的原碼爲:1000 0000 0000 1110,即-14。

 III.定點數加減運算的溢出判斷

1)用一位符號位判斷溢出

對於加法,只有在正數加正數和負數加負數兩種情況下才可能出現溢出,符號不同的兩個數相加是不會溢出的。

對於減法,只有在正數減負數和負數減正數兩種情況下才可能出現溢出,符號相同的兩個數相減是不會溢出的。

由於減法運算在機器中是用加法器實現的,因此:不論是作加法還是減法,只要實際操作數(減法時即爲被減數和“求補”之後的減數)的補碼符號位相同,而結果的符號位又與操作數補碼符號位不同,即爲溢出。如:

在4位機中,A=5,B=-4,則A-B溢出,推導過程如下:

A的原碼爲0101,補碼爲0101;-B的原碼爲0100,補碼爲0100; A-B的補碼爲1001,結果的符號位爲1,實際操作數的符號位爲0,因此溢出。

2)用兩位符號位判斷溢出

此時判斷溢出的原則是:當2位符號位不同時,表示溢出;否則無溢出。不論是否發生溢出,高位符號位永遠代表真正的符號。如:

x=-0.1011,y=-0.0111,則x+y溢出,推導過程如下:

x的原碼爲11.1011,補碼爲11.0101;y的原碼爲11.0111,補碼爲11.1001,因此x+y的補碼爲1 10.1110,將符號位產生的進位丟掉,則結果爲10.1110,因此溢出。

注:約定整數的符號位與數值位之間用逗號隔開,小數的符號位與數值位之間用小數點隔開。

 IV.浮點數

定點數表示法的缺點在於其形式過於僵硬,固定的小數點位置決定了固定位數的整數部分和小數部分,不利於同時表達特別大或特別小的數,最終,絕大多數現代的計算機系統採納了浮點數表達方式,這種表達方式利用科學計數法來表達實數,即用一個尾數(Mantissa,尾數有時也稱爲有效數字,它實際上是有效數字的非正式說法),一個基數(Base),一個指數(Exponent)以及一個表示正負的符號來表達實數,比如123.45用十進制科學計數法可以表示爲1.2345x102,其中1.2345爲尾數,10爲基數,2爲指數。浮點數利用指數達到了浮動小數點的效果,從而可以靈活地表達更大範圍的實數。

1) IEEE浮點數

在IEEE標準中,浮點數是將特定長度的連續字節的所有二進制位分割爲特定寬度的符號域、指數域和尾數域這三個域,域中的值分別用於表示給定二進制浮點數中的符號、指數和尾數,這樣,通過尾數和可以調節的指數就可以表達給定的數值了。

IEEE754指定了:

兩種基本的浮點格式:單精度和雙精度。其中單精度格式具有24位有效數字(即尾數)精度,總共佔用32位;雙精度格式具有53位有效數字(即尾數)精度,總共佔有64位       。

兩種擴展浮點格式:單精度擴展和雙精度擴展。此標準並未規定這些格式的精確精度和大小,但指定了最小精度和大小,例如IEEE雙精度擴展格式必須至少具有64位有效數字精度,並總共佔用至少79位。

具體的格式參見下面的圖例:

 

2) 單精度格式

IEEE單精度格式由三個字段組成:23位小數f、8位偏置指數e以及1位符號s,這些字段連續存儲在一個32位字中,如下圖所示:

0:22位包含23位小數f,其中第0位是小數的最低有效位,第22位是最高有效位。IEEE標準要求浮點數必須是規範的(浮點數的規範化見後文),這意味着尾數的小數點左側必須位1,因此我們在保存尾數時,可以省略小數點前面的1,從而騰出一個二進制位來保存更多的尾數,這樣我們實際上用23位長的尾數域表達了24位的尾數。

23:30位包含8位偏置指數3,第23位是偏置指數的最低有效位,第30位是最高有效位。8位的指數可以表達0到255之間的256個指數值,但指數可以位正數,也可以爲負數,因此爲了處理負指數的情況,實際的指數值按要求需要加上一個偏置(Bias)值作爲保存在指數域中的值,單精度的偏置值爲127(2^7-1),比如單精度的實際指數值0在指數域中保存爲127(0+127),實際指數值-63在指數域中保存爲64(-63+127)。偏置的引入使得對於單精度數,實際可以表達的指數值的範圍變爲-127到128之間(包含兩端),其中指數值-127(保存爲全0)以及+128(保存爲全1)保留用作特殊值的處理,稍後介紹。如果我們分別用emin和emax來表達其它常規指數值範圍的邊界,即最小指數和最大指數分別用emin和emax來表示,即-126和127,則保留的特殊指數值可以分別表達爲emin-1和emax+1;

最高的第31位包含符號位s,s爲0表示數值爲正數,s位1表示數值爲負數。

值得注意的是,對於單精度數,由於我們只有24位的尾數(小數點左側的1被隱藏),所以可以表達的最大尾數爲2^24-1=16,777,215,因此單精度的浮點數可以表達的十進制數值中,真正有效的數字不高於8位。

3) 雙精度格式

IEEE雙精度格式由三個字段組成:52位小數f、11位偏置指數e以及1位符號s,這些字段連續存儲在兩個32位字中,如下圖所示:

在SPARC體系結構中,較高地址的32位字包含小數的32位最低有效位,而在x86體系結構中,則是較低地址的32位字包含小數的32位最低有效位。

以x86體系結構爲例,則f[31:0]表示小數的32位最低有效位,其中第0位是整個小數的最低有效位。在另一個32位字中,0:19位表示小數的20位最高有效位f[51:32],其中第19位是整個小數的最高有效位;20:30位包含11位偏置指數e,其中第20位是偏置指數的最低有效位,第30位是偏置指數的最高有效位;第31位則是符號位s。上圖將這兩個連續的32位字按一個64位字那樣進行了編號,其中:

0:51位包含52位小數f,其中第0位是小數的最低有效位,第51位是小數的最高有效位。IEEE標準要求浮點數必須是規範的,這意味着尾數的小數點左側必須爲1,因此我們在保存尾數時,可以省略小數點前面的1,從而騰出一個二進制位來保存更多的尾數,這樣我們實際上用52位長的尾數域表達了53位的尾數。

52:62位包含11位偏置指數e,第52位是偏置指數的最低有效位,第 62 位是最高有效位。11 位的指數爲可以表達 0 到 2047 之間的2048個指數值,但指數可以爲正數,也可以爲負數,因此爲了處理負指數的情況,實際的指數值按要求需要加上一個偏差(Bias)值作爲保存在指數域中的值,單精度數的偏差值爲1023(2^10-1),偏差的引入使得對於單精度數,實際可以表達的指數值的範圍就變成 -1023到1024之間(包含兩端)。最小指數和最大指數分別用emin 和 emax來表達,稍後將介紹實際的指數值 -1023(保存爲全0)以及 +1024(保存爲全 1)保留用作特殊值的處理。

最高的第 63 位包含符號位s,s爲0表示數值爲正數, s爲1則表示負數。

4) 雙精度擴展格式(SPARC)

SPARC浮點環境的雙精度格式符合IEEE關於雙精度擴展格式的定義。

SPARC浮點環境的雙精度格式包含三個字段:112位小數f、15位偏置指數e以及1位符號s,這三個字段連續存儲,如下圖所示:

地址最高的32位字包含小數的32位最低有效位,用f[31:0]表示;緊鄰的兩個32位字分別包含f[63:32]和f[95:64];接下來的32位字中, 0:15 位包含小數的16位最高有效位f[111:96],其中第15位是整個小數的最高有效位;16:30位包含15位偏置指數e,其中第16位是該偏置指數的最低有效位,第30位是偏置指數的最高有效位;第 31位包含符號位s。

上圖將這四個連續的32位字按一個128位字那樣進行了編號,其中0:111位存儲小數f;112:126位存儲15位偏置指數e而第 127 位存儲符號位 s。

5) 雙精度擴展格式(x86)

x86浮點環境的雙精度格式符合IEEE關於雙精度擴展格式的定義。

x86浮點環境的雙精度格式包含四個字段:63位小數f、1位顯式前導有效數位j、15位偏置指數e以及1位符號s。在 x86 體系結構系列中,這些字段連續存儲在十個相連地址的8位字節中,由於UNIX System V Application Binary Interface Intel 386 Processor Supplement (Intel ABI) 要求雙精度擴展參數,從而佔用堆棧中三個相連地址的32位字,其中地址最高字的16位最高有效位未用,如下圖所示:

地址最低的32位字包含小數的32位最低有效位 f[31:0],其中第0位是整個小數的最低有效位;地址居中的 32 位字中,0:30位包含小數的31位最高有效位 f[62:32],其中第30位是整個小數的最高有效位,第31位包含顯式前導有效數位 j。

地址最高的32位字中,0:14位包含15位偏置指數e,其中第0位是該偏置指數的最低有效位,而第14位是最高有效位;第15位包含符號位s。

VI.浮點數的規範化

同樣的數值可以有多種浮點數表達方式,比如上面例子中的123.45可以表達爲12.345x10^1,0.12345x10^3或者1.2345x10^2,因爲這種多樣性,有必要對其加以規範化以達到統一表達的目標。規範的(Normalized)浮點數表達方式具有如下形式:

±d.dd...d ×β, (0≤d i<β)

其中d.dd...d爲尾數,β爲基數,e爲指數。尾數中數字的個數稱爲精度,用 p 來表示,每個數字d介於0和基數之間,包括0,小數點左側的數字不爲0

基於規範表達的浮點數對應的具體值可由下面的表達式計算得到:

±(d + d 1β-1 + ... + d p-1β-(p-1), (0≤d i<β)

對於十進制的浮點數,即基數β等於10的浮點數而言,上面的表達式非常容易理解,也很直白。而計算機內部的數值表達是基於二進制的,從上面的表達式,我們可以知道二進制數同樣可以有小數點,也同樣具有類似於十進制的表達方式,只是此時β等於2,而每個數字d只能在0和1之間取值,比如二進制數 1001.101相當於1x2^3+0x2^2+0x2^1+1x2^0+1x2^-1+0x2^-2+1x2^-3,對應於十進制的 9.625,其規範浮點數表達爲1.001101x2^3。

6) 實數和浮點數之間的轉換

假定我們有一個32位的數據,它是一個單精度浮點數,十六進制表示爲0xC0B40000,爲了得到該浮點數實際表達的實數,我們首先將其轉換爲二進制形式:

1100 0000 1011 0100 0000 0000 0000 0000

接着按照浮點數的格式切分爲相應的域:

1 1000_0001 0110_1000_0000_0000_0000_000

符號位1表示這是一個負數,指數域爲129,意味着實際值爲2,尾數域爲01101意味着實際的二進制尾數爲1.01101,所以實際的實數爲:

-1.01101x2^2=-101.101=-5.625

從實數向浮點數變換稍微麻煩一點,假定我們需要將實數-9.625表達爲單精度的浮點數格式,方式是首先將它用二進制浮點數表示,然後變換爲相應的浮點數格式。

首先,整數部分,即9的二進制形式爲1001,小數部分的算法則是將小數部分連續乘以基數2,並記錄結果的整數部分:

0.625x2=1.25     1

0.25x2=0.5         0

0.5x2=1              1

當最後的小數部分爲0時,結束該過程,因此小數部分的二進制表達爲0.101,這樣我們就得到了完整的二進制形式1001.101,用規範浮點數表示爲:

1.001101x2^3

因爲是負數,因此符號位爲1,指數爲3,因此指數域爲3+127=130,即二進制的1000 0010,尾數域省掉小數點左側的1,右側用零補齊,得到最終結果爲:1 1000_0010 0011_0100_0000_0000_0000_000,最後可以將浮點數表示爲十六進制的數據如下:1100 0001 0001 1010 0000 0000 0000 0000,最終結果爲0xC11A0000  

這裏需要注意一個問題,在上面我們有意選擇的示例中,不斷地將產生的小數部分乘以2的過程掩蓋了一個事實,該過程結束的標誌是小數部分乘以2的結果爲1,但實際上,很多小數根本不能經過有限次這樣的過程而得到結果(比如0.1),但浮點數尾數域的位數是有限的,爲此,浮點數的處理方法是持續該過程直到由此得到的尾數足以填滿尾數域,之後對多餘的位進行舍入。換句話說,除了我們之前講到的精度問題之外,十進制到二進制的變換也並不能保證總是精確的,而只能是近似值。事實上,只有很少一部分十進制小數具有精確的二進制浮點數表達,再加上浮點數運算過程中的誤差累積,結果是很多我們看來非常簡單的十進制運算在計算機上卻往往出人意料,這就是最常見的浮點運算的"不準確"問題,比如:34.6f-34.0f=0.599998,產生這個誤差的原因是34.6f無法精確的表達爲相應的浮點數,而只能保存爲經過舍入的近似值,這個近似值與34.0f之間的運算自然無法產生精確的結果(具體過程後面會講)。

7) 舍入

根據標準要求,無法精確保存的值必須向最接近可保存的值進行舍入,這有點像我們熟悉的十進制的四捨五入,即不足一半則舍,一半以上(包括一半)則進,不過對於二進制浮點數而言,則是0就舍,但1不一定進,而是在前後兩個等距接近的可保存的值中,取其中最後一位有效數字爲零的值進行保存,即採取向偶數舍入,比如0.5要舍到0,1.5要入到2(即先試着進1,會得到最後結果,如果這個結果的尾數的最後位爲0,則進位成功;否則進位失敗,直接捨去),看下面幾個例子:

結果推導分析:

16777215f的分析過程:

1111 1111 1111 1111 1111 1111                         16777215f(其中第一個1會隱藏)

1.1111_1111 1111_1111_1111_111                 剛好是23位小數,不會丟失精度,能精確表示

0 23+127 1111_1111_1111_1111_1111_111   (0表示是正數,23+127表示偏移指數值,尾數位爲23個1)

0 10010110 1111_1111_1111_1111_1111_111

 

16777216f的分析過程:

1000 0000 0000 0000 0000 0000 0                    16777216f(最後一個0會被捨去)

1.0000_0000_0000_0000_0000_000 0              (因爲尾數域爲23位,因此最後一個0被捨去,但是還是能準確顯示)

0 24+127 0000_0000_0000_0000_0000_000

0 10010111 0000_0000_0000_0000_0000_000

 

16777217f的分析過程:

1000 0000 0000 0000 0000 0000 1                    16777217f(最後一個1會被捨去)

1.0000_0000_0000_0000_0000_000 1              (最後一個1被捨去,試着進位)

1. 0000_0000_0000_0000_0000_001                (由於進位後,尾數域末尾不爲0,因此進位失敗,直接將1捨去)

1. 0000_0000_0000_0000_0000_000                (這裏其實已經說明結果爲16777216f)

 

16777218f的分析過程:

1000 0000 0000 0000 0000 0001 0                    16777218f(最後一個0會被捨去)

1. 0000_0000_0000_0000_0000_001 0             (最後一個0被捨去,所以還是能準確顯示)

0 24+127 0000_0000_0000_0000_0000_001

0 10010111 0000_0000_0000_0000_0000_001

 

16777219f的分析過程:

1000 0000 0000 0000 0000 0001 1                    16777219f(最後一個1會被捨去)

1. 0000_0000_0000_0000_0000_001 1             (最後一個1被捨去,嘗試進位)

1. 0000_0000_0000_0000_0000_010                (尾數域末位爲0,進位成功)

0 24+127 0000_0000_0000_0000_0000_010

010010111 0000_0000_0000_0000_0000_010

… …

8) 浮點數的加減運算

浮點數的加減運算一般由以下五個步驟完成:對階、尾數運算、結果規格化、舍入處理、溢出判斷。

tip1.對階

對階的目的是使兩操作數的小數點位置對齊,即使兩數的階碼相等。爲此,首先要求出階差,再按小階向大階看齊的原則,使階小的尾數向右移位,每右移一位,階碼加1,直到兩數的階碼相等爲止。

tip2.尾數運算

尾數運算就是將對階後的尾數按定點加減運算規則進行運算。

tip3.結果規格化

在機器中,爲保證浮點數表示的唯一性,浮點數在機器中都是以規格化形式存儲的。對於IEEE754標準的浮點數來說,就是尾數必須是1.xxxx的形式。由於在進行上述兩個定點小數的尾數相加減運算後,尾數有可能是非規格化形式,爲此必須進行規格化操作,規格化操作包括左規和右規兩種情況。

左規:將尾數左移一位,同時階碼減1,直至尾數成爲1.xxxx的形式;

右規:將尾數右移一位,同時階碼增1,便成爲規格化的形式了。

注:右規操作只需將尾數右移一位即可,這種情況出現在尾數的最高位(即小數點前一位)運算時出現了進位,使尾數成爲10.xxxx或11.xxxx的形式。

tip4.舍入處理

在對階和右規過程中,可能會將尾數的低位丟失,引起誤差,影響精度,爲此可用舍入法來提高尾數的精度。IEEE754標準列出了四種可選的舍入處理方法:  

就近舍入(round to neareset):這是標準列出的默認舍入方式,前面有講。

朝+∞舍入(round toward +∞):對正數來說,只要多餘位不爲全0,則向尾數最低有效位進1;對負數來說,則是簡單地捨去。  

朝-∞舍入(round toward -∞):與朝+∞舍入方法正好相反,對正數來說,只是簡單地捨去;對負數來說,只要多餘位不爲全0,則向尾數最低有效位進1。  

朝0舍入(round toward 0): 即簡單地截斷捨去,而不管多餘位是什麼值。這種方法實現簡單,但容易形成累積誤差,且舍入處理後的值總是向下偏差。

tip5.溢出判斷

與定點數運算不同的是,浮點數的溢出是以其運算結果的階碼的值是否產生溢出來判斷的。若階碼的值超過了階碼所能表示的最大正數,則爲上溢,進一步,若此時浮點數爲正數,則爲正上溢,記爲+∞,若浮點數爲負數,則爲負上溢,記爲-∞;若階碼的值超過了階碼所能表示的最小負數,則爲下溢,進一步,若此時浮點數爲正數,則爲正下溢,若浮點數爲負數,則爲負下溢。正下溢和負下溢都作爲機器零處理,即將尾數各位強置爲零。  

/*浮點數加減運算-例1*/

設兩浮點數的IEEE754標準存儲格式分別爲  

x=0 10000010 01101100000000000000000,y=0 10000100 01011101100000000000000,求x+y,並給出結果的IEEE754標準存儲格式。

解:

對於浮點數x,

符號位s=0

指數e=E-127= 10000010-01111111=00000011=(3)10  

尾數m=1.01101100000000000000000

於是有 x=+1.01101100000000000000000×23   

對於浮點數y:  

符號位s=0  

指數e=E-127=10000100-01111111=00000011=(5)10 

尾數m=1.01011101100000000000000

於是有  

y=+1.01011101100000000000000×2

(1)對階

⊿E=Ex-Ey=3-5=-2 ,因此x=1.01101100000000000000000×23

0.01011011000000000000000×2

 (2)尾數相加  

x+y=00.01011011000000000000000×2+01.01011101100000000000000×25  

       =01.10111000100000000000000×25(其中最高位是符號位)

 結果的IEEE754標準存儲格式爲:0 10000100 10111000100000000000000 

 

/*浮點數加減運算-例2*/

求34.6f-34.0f

解:

對於浮點數34.6f:

符號位s=0

指數e=(5)10 

尾數m=1. 000101001100110011001100…,根據就近舍入,得到:

m'=1. 00010100110011001100110

對於浮點數34.0f:

符號位s=0

指數e=(5)10 

尾數m=1.00010000000000000000000

(1)對階

階碼相同

(2)尾數相加  

34.6f-34.0f = 01. 00010100110011001100110×25 -11.00010000000000000000000×25

                     01. 00010100110011001100110×25 + 10.11110000000000000000000×25

100.00000100110011001100110×25

00.00000100110011001100110×25(符號位的進位1被捨去)

(3)左規  

34.6f-34.0f = 1.00110011001100110000000×2-1 = (0.59999847)10

結果的IEEE754標準存儲格式爲:0 01111110 00110011001100110000000

 

/*浮點數加減運算-例3*/

求1.5f-2.4f

解:

對於浮點數1.5f:

符號位s=0

指數e=(0)10 

尾數m=1.10000000000000000000000

對於浮點數2.4f:

符號位s=0

指數e=(1)10 

尾數m=1.00110011001100110011010

(1)對階

⊿E=0-1=-1 ,因此1.5f=0.110000000000000000000000×21

(2)尾數相加  

1.5f-2.4f = 00.110000000000000000000000×21 - 11.00110011001100110011010×2      

00.110000000000000000000000×21 + 10.11001100110011001100110×2 

11.10001100110011001100110×21(其中最高位是符號位)

10.01110011001100110011010×21(將補碼轉換成原碼)

(3)左規  

1.5f-2.4f = -1.11001100110011001101000×2-1 = (-0.90000010)10

結果的IEEE754標準存儲格式爲:1 01111110 11001100110011001101000

9) 特殊值-NaN

當指數爲128(指數域全1),且尾數域不等於0時,該浮點數即爲NaN。

IEEE標準沒有要求具體的尾數域,所以NaN實際上不是一個,而是一族。

比較操作符<、<=、>、>=在任一操作數爲NaN時均返回false,等於操作符==在任一操作數爲NaN時均返回false,即使是兩個具有相同位模式的NaN也一樣,而操作符!=則當任一操作數爲NaN時均返回true,這個規則的一個有趣的結果是x!=x,當x爲NaN時竟然爲真。

用特殊的NaN來表達上述運算錯誤的意義在於避免了因這些錯誤而導致運算的不必要的終止。比如,如果一個被循環調用的浮點運算方法,可能由於輸入的參數問題而導致發生這些錯誤,NaN使得即使某次循環發生了這樣的錯誤,也可以簡單地繼續執行循環以進行那些沒有錯誤的運算。你可能想到,既然Java有異常處理機制,也許可以通過捕獲並忽略異常達到相同的效果。但是,要知道,IEEE標準不是僅僅爲Java而制定的,各種語言處理異常的機制不盡相同,這將使得代碼的遷移變得更加困難。何況,不是所有語言都有類似的異常或者信號(Signal)處理機制。

10) 特殊值-無窮

當指數爲128(指數域全1),且尾數域等於0時,該浮點數即爲無窮大,用符號位來確定是正無窮大還是負無窮大。

無窮用於表達計算中產生的上溢(Overflow)問題。比如兩個極大的數相乘時,儘管兩個操作數本身可以用保存爲浮點數,但其結果可能大到無法保存爲浮點數,而必須進行舍入。根據IEEE標準,此時不是將結果舍入爲可以保存的最大的浮點數(因爲這個數可能離實際的結果相差太遠而毫無意義),而是將其舍入爲無窮。對於負數結果也是如此,只不過此時舍入爲負無窮,也就是說符號域爲1的無窮。有了NaN的經驗我們不難理解,特殊值無窮使得計算中發生的上溢錯誤不必以終止運算爲結果。

無窮和除NaN以外的其它浮點數一樣是有序的,從小到大依次爲負無窮,負的有窮非零值,正負零(隨後介紹),正的有窮非零值以及正無窮。除NaN以外的任何非零值除以零,結果都將是無窮,而符號則由作爲除數的零的符號決定。

回顧我們對NaN的介紹,當零除以零時得到的結果不是無窮而是NaN。原因不難理解,當除數和被除數都逼近於零時,其商可能爲任何值,所以IEEE標準決定此時用NaN作爲商比較合適。

11) 特殊值-有符號的零

因爲IEEE標準的浮點數格式中,小數點左側的1是隱藏的,而零顯然需要尾數必須是零。所以,零也就無法直接用這種格式表達而只能特殊處理。

當指數爲-127(指數域全0),且尾數域等與0時,該浮點數即爲零,考慮到符號域的作用,所以存在着兩個零,即+0和-0。不同於正負無窮之間是有序的,IEEE標準規定正負零是相等的。

零有正負之分,的確非常容易讓人困惑,這一點是基於數值分析的多種考慮,經利弊權衡後形成的結果。有符號的零可以避免運算中,特別是涉及無窮的運算中,符號信息的丟失。舉例而言,如果零無符號,則等式1/(1/x)=x當x=±∞時不再成立。原因是如果零無符號,1和正負無窮的比值爲同一個零,然後1與0的比值爲正無窮,符號沒有了。解決這個問題,除非無窮也沒有符號,但是無窮的符號表達了上溢發生在數軸的哪一側,這個信息顯然是不能不要的,因此零有符號。

public class test

{

      public static void main(String[] args)

      {

             //指數域全0,尾數域爲0時,浮點數爲有符號的零

             System.out.println(Float.intBitsToFloat(0x0));

             //指數域全1,尾數域爲0時,浮點數爲無窮,符號位決定是正無窮還是負無窮

             System.out.println(Float.intBitsToFloat(0x7F800000));

             System.out.println(Float.intBitsToFloat(0xFF800000));

             //指數域全1,尾數域不爲0時,浮點數爲NaN

             System.out.println(Float.intBitsToFloat(0x7FC00000));

             System.out.println(Float.intBitsToFloat(0x7F800001));

             System.out.println(Float.intBitsToFloat(0xFF800001));

      }

}

解釋一下幾個方法:

Float.floatToIntBits(float f):按照IEEE754標準,返回指定浮點數的表達形式。舉個例子:Float.floatToIntBits(20.5f)按照如下方式計算:20.5D=10100.1B=1.01001x2^4,因此可將浮點數20.5f表示爲十六進制數據:0100 0001 1010 0100 0000 0000 0000 0000,轉換成10進制即1101266944

Float.intBitsToFloat(int i):按照IEEE754標準,返回指定整數的表達形式。舉個例子:Float.intBitsToFloat(1101266944)即爲20.5f

Integer.toBinaryString(int i):將指定整數表示成相應的二進制數,舉個例子:

Ineger.toBinaryString(128)即爲10000000

注:javac編譯編碼GBK的不可映射字符時,可以通過-encoding來指定編碼方式,如:

javac -encoding utf-8 test.java

12) 非規範化數

這個數的定義和有符號0一樣,不過尾數不能爲0,用於小出範圍的數。

我們來考察浮點數的一個特殊情況。選擇兩個絕對值極小的浮點數,以單精度的二進制浮點數爲例,比如1.001×2^-125和1.0001×2^-125這兩個數(分別對應於十進制的2.6448623×10^-38和2.4979255×10^-38)。顯然,他們都是普通的浮點數(指數爲-125,大於允許的最小值-126;尾數更沒問題),按照IEEE754可以分別保存爲0 00000010 00100000000000000000000(0x1100000)和0 00000010 00010000000000000000000(0x1080000)。

現在我們看看這兩個浮點數的差值。

不難得出,該差值爲0.0001×2^-125,表達爲規範浮點數則爲1.0×2^-129。問題在於其指數小於允許的最小指數值,所以無法保存爲規範浮點數,最終只能近似爲零(FlushtoZero)。這種特殊情況意味着下面本來十分可靠的代碼也可能出現問題:

if(x!=y)

{

  z=1/(x-y);

}

正如我們精心選擇的兩個浮點數展現的問題一樣,即使x不等於y,x和y的差值仍然可能絕對值過小,而近似爲零,導致除以0的情況發生。

爲了解決此類問題,IEEE標準中引入了非規範(Denormalized)浮點數。規定當浮點數的指數爲允許的最小指數值,即emin時,尾數不必是規範化的。比如上面例子中的差值可以表達爲非規範的浮點數0.001×2^-126,其中指數-126等於emin。爲了保存非規範浮點數,IEEE標準採用了類似處理特殊值零時所採用的辦法,即用特殊的指數域值emin-1加以標記,當然,此時的尾數域不能爲零。這樣,例子中的差值可以保存爲00000000000100000000000000000000(0x100000),沒有隱含的尾數位。

有了非規範浮點數,去掉了隱含的尾數位的制約,可以保存絕對值更小的浮點數。而且,也由於不再受到隱含尾數域的制約,上述關於極小差值的問題也不存在了,因爲所有可以保存的浮點數之間的差值同樣可以保存。

注意,規定的是"不必",這也就意味着"可以",因此當浮點數實際的指數爲emin,該浮點數仍是規範的,也就是說,保存時隱含着一個隱藏的尾數位。

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