計算機中的數分為整數與實數。對於實數,絕大多數現代的計算機系統採納了所謂的浮 點數表達方式。 這種表達方式利用科學計數法來表達實數,即用一個尾數(Mantissa ), 一 個基數(Base),一個指數 e(階碼 E=e+127 或者 e+1023)(exponent)以及一個表示正負 的符號(Sign)來表達實數。 比如 123.45 用十進位科學計數法可以表達為 1.2345 × 10^2 , 其中 1.2345 為尾數,10 為基數,2 為指數。 浮點數利用指數達到了浮動小數點的效果, 從而可以靈活地表達更大範圍的實數。 又對於一個二進位的數比如 1011.01,用科學計數 法也可以表示為:1.01101*2^3,其中 1.1101 為尾數,2 為基數,3 為指數。

一,浮點數的存儲方法

計算機中是用有限的連續位元組保存浮點數的。 保存這些浮點數當然必須有特定的格式, C/C++中的浮點數類型 float 和 double 採納了 IEEE 754 標準中所定義的單精度 32 位 浮點數和雙精度 64 位浮點數的格式。 在 IEEE 標準中,浮點數是將特定長度的連續位元組 的所有二進位位分割為特定寬度的符號域,指數域和尾數域三個域, 其中保存的值分別用 於表示給定二進位浮點數中的符號,指數和尾數。 這樣,通過尾數和可以調節的指數(所 以稱為"浮點")就可以表達給定的數值了。

根據國際標準 IEEE 754,任意一個二進位浮點數 V 可以表示成下面的形式:

V = (-1) ^ s × M × 2 ^ E

(1)(-1)^s 表示符號位,當 s=0,V 為正數;當 s=1,V 為負數。

(2)M 表示有效數字,大於等於 1,小於 2,但整數部分的 1 不變,因此可以省略。

(3)2^E 表示指數位。

比如: 對於十進位的 5.25 對應的二進位為:101.01,相當於:1.0101*2^2。所以,S 為 0,M 為 1.0101,E 為 2。 而-5.25=-101.01=-1.0101 *2^2.。所以 S 為 1,M 為 1.0101,E 為 2。

對於 32 位的單精度數來說,從低位到高位,尾數 M 用 23 位來表示,階碼 E 用 8 位來表示, 而符號位用最高位 1 位來表示,0 表示正,1 表示負。對於 64 位的雙精度數來說,從低位 到高位,尾數 M 用 52 位來表示,階碼用 11 位來表示,而符號位用最高位 1 位來表示,0 表示正,1 表示負。

IEEE 754 對有效數字 M 和指數 E,還有一些特別規定。 前面說過,M 可以寫成 1.xxxxxx 的形式,其中 xxxxxx 表示小數部分。IEEE 754 規定,在計算機內部保存 M 時,默認這個 數的第一位總是 1,因此可以被捨去,只保存後面的 xxxxxx 部分。比如保存 1.0101 的時候, 只保存 0101,等到讀取的時候,再把第一位的 1 加上去。這樣做的目的,是節省 1 位有效 數字。以 32 位浮點數為例,留給 M 只有 23 位,將第一位的 1 捨去以後,等於可以保存 24 位有效數字。

對於 E, 首先,E 為一個無符號整數(unsigned int)。這意味著,如果 E 為 8 位,它的取 值範圍為 0~255;如果 E 為 11 位,它的取值範圍為 0~2047。然而科學計數法中的 E 是可 以出現負數的,所以 IEEE 754 規定,E 的真實值必須再減去一個中間數,對於 8 位的 E, 這個中間數是 127;對於 11 位的 E,這個中間數是 1023。 比如,2^2 的 E 是 2,所以保 存成 float 32 位浮點數時,必須保存成 2+127=129,即 10000001。

此外,E 還需要考慮下面 3 種情況:

(1)E 不全為 0 或不全為 1。這時,浮點數就採用上面的規則表示,即指數 E 的計算值減 去 127(或 1023),得到真實值,再將有效數字 M 前加上第一位的 1。

(2)E 全為 0。這時,浮點數的指數 E 等於 1-127(或者 1-1023),有效數字 M 不再加上 第一位的 1,而是還原為 0.xxxxxx 的小數。這樣做是為了表示±0,以及接近於 0 的很小的 數字。

(3)E 全為 1。這時,如果有效數字 M 全為 0,表示±無窮大(正負取決於符號位 s);如 果有效數字 M 不全為 0,表示這個數不是一個數(NaN)。

二,浮點數的轉換方法

浮點數的轉換方法可以分為如下 2 種情況:

1.給出一個浮點數,計算對應的二進位 比如給定一個浮點數,7.25,如何計算它對應的單精度和雙精度的二進位呢?

首先,十進位浮點數 7.25 對應的二進位(二進位,十進位和十六進位轉化方法:點擊這裡) 為:111.01。用二進位的科學計數法為:1.1101*2^2。所以,按照上面浮點數的存儲結構, 得出符號位為: 0,表示正數;階碼(指數) E 單精度為 2+127=129,雙精度為 2+1023=1025; 小數部分 M 為:1101。 所以,

單精度的二進位位:0 10000001 1101 0000000000000000000;

雙 精 度 的 二 進 制 位 : 0 10000000001 1101 000000000000000000000000000000000000000000000000

第一步:將 178.125 表示成二進位數:(178.125)(十進位數)=(10110010.001)(二進位形式);

第二步:將二進位形式的浮點實數轉化為規格化的形式:(小數點向左移動 7 個二進位位可以 得到)

10110010.001=1.0110010001*2^7 因而產生了以下三項:

符號位:該數為正數,故第 31 位為 0,佔一個二進位位.

階碼:指數(e)為 7,故其階碼為 127+7=134=(10000110)(二進位),佔從第 30 到第 23 共 8 個 二進位位.

(註:指數有正負即有符號數,但階碼為正即無符號數,所以將 e 加個 127 作為偏移,方 便指數的比較)

尾數為小數點後的部分, 即 0110010001.因為尾數共 23 個二進位位,在後面補 13 個 0,即 01100100010000000000000

所以,178.125 在內存中的實際表示方式為:

0 10000110 01100100010000000000000

2.給出一個浮點數的二進位,計算對應的十進位值

而如果而如果給出了一個浮點數的二進位,如何計算它對應的十進位,其實就是 1 中的逆 運算。分別求出對應的符號位,階碼指數 E 和小數 M 部分,就可以了。比如,給定一個單 精度浮點數的二進位存儲為: 0 10000001 1101 0000000000000000000; 那麼對應的符號為:0,表示正數;階碼 E 為:129-127=2;尾數為 1.1101。所以對應的二 進位科學計數法為:1.1101*2^2,也就是 111.01 即:7.25。

小數的輸出

小數也可以使用 printf 函數輸出,包括十進位形式和指數形式,它們對應的格式控制符分別是:

%f 以十進位形式輸出 float 類型;

%lf 以十進位形式輸出 double 類型;

%e 以指數形式輸出 float 類型,輸出結果中的 e 小寫;

%E 以指數形式輸出 float 類型,輸出結果中的 E 大寫;

%le 以指數形式輸出 double 類型,輸出結果中的 e 小寫;

%lE 以指數形式輸出 double 類型,輸出結果中的 E 大寫。

對代碼的說明:

%f 和 %lf 默認保留六位小數,不足六位以 0 補齊,超過六位按四捨五入截斷。

將整數賦值給 float 變數時會變成小數。

以指數形式輸出小數時,輸出結果為科學計數法;也就是說,尾數部分的取值為:0 ≤ 尾數 < 10。

另外,小數還有一種更加智能的輸出方式,就是使用%g。%g 會對比小數的十進位形式和指數形式,以最短的方式來輸出小數,讓輸出結果更加簡練。所謂最短,就是輸出結果佔用最少的字元。

%g 使用示例:

運行結果:

a=1e-05

b=3e+07

c=12.84

d=1.22934

對各個小數的分析:

a 的十進位形式是 0.00001,佔用七個字元的位置,a 的指數形式是

1e-05,佔用五個字元的位置,指數形式較短,所以以指數的形式輸出。

b 的十進位形式是 30000000,佔用八個字元的位置,b 的指數形式是 3e+07,佔用五個字元的位置,指數形式較短,所以以指數的形式輸出。

c 的十進位形式是 12.84,佔用五個字元的位置,c 的指數形式是 1.284e+01,佔用九個字元的位置,十進位形式較短,所以以十進位的形式輸出。

d 的十進位形式是 1.22934,佔用七個字元的位置,d 的指數形式是 1.22934e+00,佔用十一個字元的位置,十進位形式較短,所以以十進位的形式輸出。

讀者需要注意的兩點是:

%g 默認最多保留六位有效數字,包括整數部分和小數部分;%f 和 %e 默認保留六位小數,只包括小數部分。

%g 不會在最後強加 0 來湊夠有效數字的位數,而 %f 和 %e 會在最後強加 0 來湊夠小數部分的位數。

總之,%g 要以最短的方式來輸出小數,並且小數部分表現很自然,不會強加零,比 %f 和 %e 更有彈性,這在大部分情況下是符合用戶習慣的。

除了 %g,還有 %lg、%G、%lG:

%g 和 %lg 分別用來輸出 float 類型和 double 類型,並且當以指數形式輸出時,e小寫。

%G 和 %lG 也分別用來輸出 float 類型和 double 類型,只是當以指數形式輸出時,E大寫。

---------------------

作者:洛銘

來源:CSDN

原文:浮點數格式與存儲 - weixin_42528287的博客 - CSDN博客

版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


推薦閱讀:
相關文章