目錄結構:ios
IEEE 754(Institute of Electrical and Electronics Engineers)在1985年發佈,該標準是爲了統一規範浮點數的存儲。less
在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)
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
數值0被特殊表示:
符號位(sign) = 0或1
有偏指數(biased exponent) = 0
小數(fraction)= 0
0的內存二進制碼爲:
0 00000000 00000000000000000000002
1 00000000 00000000000000000000002
有一些算數操做是非法的,好比對負數開根號。這類非法操做被稱爲浮點數異常(floating-point exception),異常結果由特殊字符NaN(Not a Number)表示。
符號位(sign) = 0或1
有偏指數(biased exponent)= 全部位都是1
小數(fraction) = 除了全部位都是0的數(由於全部爲0,表示無窮大)
小數位只要不全爲0,就表示非數值。
0 11111111 111111111111000000100002
或
1 11111111 111111111111000000100002
無窮大有兩種,正無窮大(Positive Infinity)和負無窮大(Negative Infinity)。
符號位(sign) = 0表示正無窮大,1表示負無窮大。
有偏指數(biased exponent) = 全部位都是1
小數(fraction) = 全部位都是0.
正無窮大
0 11111111 000000000000000000000002
負無窮大
1 11111111 000000000000000000000002
若是計算機是採用的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
在學習過上面的知識後,咱們清楚了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 |
在單精度浮點數中的二進制小數位有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
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?