【算法】解析IEEE 754 標準

目錄結構:ios

contents structure [-]

IEEE 754(Institute of Electrical and Electronics Engineers)在1985年發佈,該標準是爲了統一規範浮點數的存儲。less

 

1.浮點數的存儲過程

在IEEE 754標準中浮點數由三部分組成:符號位(sign bit),有偏指數(biased exponent),小數(fraction)。浮點數分爲兩種,單精度浮點數(single precision)和雙精度浮點數(double precision),它們兩個所佔的位數不一樣。學習

 

單精度浮點數(共32位):
1個符號位
8個指數位
23個小數位
spa

 

雙精度浮點數(共64位):
1個符號位
11個指數位
52個小數位
設計

 

接下來筆者以單精度浮點數0.15625講解浮點數的存儲過程:
0.1562510轉化爲二進制就是0.001012,而後將該數寫成科學計數法(scientific notation),根據IEEE 754的規定,小數點的左邊只能有一個1,因此最終的科學計數法形式是:code

0.1562510 = 0.001012 = 1.012 * 10-3orm

而後就能夠獲得小數部分爲.012,指數部分爲-3。blog

最終在內存中的存儲結果就是以下圖:
seo

符號位(sign):0,由於該數是正數(1表示負數)。ip

有偏指數(biased exponent):-3 + 偏移量(bias),在單精度浮點數中偏移量是127,所以127+(-3)=124,因此偏移指數是124。在雙精度浮點數中偏移量是1023,所以偏移指數是1020。

小數(fraction):.010000000000000000000002

 

在上面已經展現了浮點數的存儲過程,接下來再仔細說一說有偏指數,仍是拿單精度浮點數來講吧! 在單精度浮點數中,有8位能夠用來存儲指數(範圍就是:0~255),那麼怎麼表示負的指數呢?IEEE 754標準的制定者爲了解決這個問題,約定了指數偏移量(單精度的偏移量是127),指數值要在加上偏移量後才能進行存儲,這樣就能表示指數的正負值了。一般狀況下,若是存儲的值大於偏移量,那麼就意味着指數是正的;若是存儲的值小於偏移量,那麼就意味着指數是負的;若是存儲的值等於偏移量,那麼就意味着指數爲0。

 

下面的對應關係,顯示了有偏指數表明的各類含義:

0 == 特殊狀況:零(zero) 或 次正規數(subnormal)
1 == 2 ^ -126
    ...
125 == 2 ^ -2
126 == 2 ^ -1
127 == 2 ^  0
128 == 2 ^  1
129 == 2 ^  2
    ...
254 == 2 ^ 127
255 == 特殊狀況:無窮大(infinity) 或 非數值(NaN)

 

1.1 次正規數(Denormalized Number)

IEEE 754的設計者注意到,除了0.0全部的二進制的科學計數法都有一個1在小數點的左邊。在上面也提到過,在寫成標準的科學計數法的形式後,小數點的左邊只能有一個1。
好比:
25.010 == 110012 = 1.10012 * 24
0.62510 == 0.1012 = 1.012 * 2-1
小數點的左邊都是以一個1開始的,爲了節約內存,它們規定:全部數在小數點左邊默認有一個1。

按照這個規定的話,那麼可以表示的最小正數就是:
0 00000001 000000000000000000000012 = 1.000000000000000000000012 * 2-126

若是指數全爲0,只能表示數字0的話,那麼表示小數位的23位就沒有利用起來。因而IEEE754的設計值,規定了一種新的數 次正規數(Subnormal Number Or Denormalize Number)。規定以下:
若是指數位全爲0的話,那麼在科學計數法中小數點的左邊就默認爲一個0。這樣的數,就被稱爲次正規數。

在次正規數中全部的偏移指數位都是0,因而規定在單精度浮點數中指數應該爲-126(並不是-127),在雙精度浮點數中指數應該爲-1022(並不是-1023)

 

因此最小的正數就應該是:
0 00000000 000000000000000000000012 = 0.000000000000000000000012 * 2-126

1.2 零(zero)

數值0被特殊表示:

符號位(sign) = 0或1
有偏指數(biased exponent) = 0
小數(fraction)= 0

0的內存二進制碼爲:

0 00000000 00000000000000000000002
1 00000000 00000000000000000000002

 

1.3 非數值(NaN)

有一些算數操做是非法的,好比對負數開根號。這類非法操做被稱爲浮點數異常(floating-point exception),異常結果由特殊字符NaN(Not a Number)表示。

符號位(sign) = 0或1
有偏指數(biased exponent)= 全部位都是1
小數(fraction) = 除了全部位都是0的數(由於全部爲0,表示無窮大)

小數位只要不全爲0,就表示非數值。
0 11111111 111111111111000000100002

1 11111111 111111111111000000100002

 

1.4 無窮大(infinity)

無窮大有兩種,正無窮大(Positive Infinity)和負無窮大(Negative Infinity)。

符號位(sign) = 0表示正無窮大,1表示負無窮大。
有偏指數(biased exponent) = 全部位都是1
小數(fraction) = 全部位都是0.

正無窮大
0 11111111 000000000000000000000002
負無窮大
1 11111111 000000000000000000000002

 

2.除數爲0.0會發生什麼

若是計算機是採用的IEEE 754的標準(絕大部分計算機都是採用該標準)。那麼當除數爲0.0時,會發生不可預期的行爲(注意程序不會中斷)

#include <iostream>
#include <limits>
int main(){
//is_iec559是否支持IEC-559 / IEEE-754標準
std::cout << std::numeric_limits<float>::is_iec559 << std::endl;
std::cout << (1.0 / 0.0) << std::endl;
std::cout << (-1.0 / 0.0) << std::endl;
std::cout << (0.0 / 0.0) << std::endl;
return 0;
}

程序的輸出結果是:

1
inf
-inf
-nan

 

3.浮點數的範圍

在學習過上面的知識後,咱們清楚了IEEE 754中浮點數在內存中的表示形式,咱們也知道0(zero)是最小的(這裏和下面只討論非負數),次正規數(Denormalized Number)的表示範圍比0大,正規數(normalized Number)表示的範圍比次正規數大。

下面清楚的顯示了一些範圍和數值:


0 00000000 000000000000000000000012 = 0000 000116 = 0.12 × 2-22 × 2-126  =  2−126 × 2−23 = 2−149 ≈ 1.4012984643× 10−45 

(最小的次正規數,smallest positive subnormal number)

0 00000000 111111111111111111111112 = 007f ffff16 = 0.111111111111111111111112 * 2-126  =  2−126 × (1 − 2−23) ≈ 1.1754942107 ×10−38

(最大的次正規數,largest subnormal number)

 

0 00000001 000000000000000000000002 = 0080 000016 = 1.02 × 21-127 = 2−126 ≈ 1.1754943508 × 10−38

(最小的正正規數,smallest positive normal number)

0 11111110 111111111111111111111112 = 7f7f ffff16 = 1.111111111111111111111112  × 2254-127 =  2127 × (2− 2−23) ≈ 3.4028234664 × 1038

(最大的正正規數,largest normal number)

 

0 01111110 111111111111111111111112 = 3f7f ffff16 = 1.111111111111111111111112 × 2126-127 = 1 − 2−24 ≈ 0.9999999404

(比數值1小的最大數,largest number less than one)

0 01111111 000000000000000000000002 = 3f80 000016 = 1.02 × 2127-127  = 1.02 × 20 = 1

(數值1,one)

0 01111111 000000000000000000000012 = 3f80 000116 = 1.000000000000000000000012 × 2127-127 = 1 + 2−23 ≈ 1.0000001192

(比數值1大的最小數,smallest number larger than one)

 

1 10000000 000000000000000000000002 = c000 000016 = −2

0 00000000 000000000000000000000002 = 0000 000016 = 0

1 00000000 000000000000000000000002 = 8000 000016 = −0

 

0 11111111 000000000000000000000002 = 7f80 000016 = infinity(正無窮)

1 11111111 000000000000000000000002 = ff80 000016 = −infinity(負無窮)

 

0 10000000 100100100001111110110112 = 4049 0fdb16 ≈ 3.14159274101 ≈ π ( 圓周率,pi )

0 01111101 010101010101010101010112 = 3eaa aaab16 ≈ 0.333333343267 ≈ 1/3

 

x 11111111 100000000000000000000012 = ffc0 000116 = qNaN (on x86 and ARM processors)

x 11111111 000000000000000000000012 = ff80 000116 = sNaN (on x86 and ARM processors)

一般咱們所說的浮點數的範圍,都是指的正規數的存儲範圍。

Level Width Range at full precision
Single precision 32bits ±1.18×10−38 to ±3.4×1038
Double precision 64 bits ±2.23×10−308 to ±1.80×10308

 

4.浮點數的精度

在單精度浮點數中的二進制小數位有23個,所能表示2^23個數,那麼只須要換算成在10進制下可以表示相同個數的位數,就能夠獲得精度了。
10n = 223
10n = 8388608
106 < 8388608 < 107
因此但精度浮點數的精度爲6位,同理也能夠獲得雙精度浮點數的精度爲15位。

注意:精度爲6位,並非表示全部小於6的數均可以被精確存儲,好比0.9。由於這個精度是由二進制的精度位數計算而來的。

因此浮點數的相等判斷中,只須要判斷他們的差值小於精度就能夠了。

#include <stdio.h>      /* printf */
#include <math.h>       /* fabs */

int main ()
{
  float f1 = 0.007;
  float f2 = 0.009;

  int res = ( fabs(f1-f2) < 1e-6 );
  printf ("f1 == f2 is : %s\n",res?"true":"false");
  return 0;
}

輸出結果:

f1 == f2 is : false

 

5.參考文獻

Single-precision floating-point format_Wikipedia
IEEE 754-1985_Wikipedia
What is a subnormal floating point number?
What is a 「bias value」 of floating-point numbers?

相關文章
相關標籤/搜索