1. 程式人生 > 實用技巧 >浮點型的原理介紹及在記憶體中的儲存形式

浮點型的原理介紹及在記憶體中的儲存形式

浮點型的原理介紹及在記憶體中的儲存形式

C語言提供了浮點資料型別,單精度浮點數float和雙精度浮點數double。浮點數屬於不精確的資料型別,本文將通過float型別的原理和在記憶體中的儲存形式來介紹浮點型不精確的原因。以float型別為例,來展示C語言中浮點型的神祕之處。

float型別介紹

float是baiC語言的基本資料型別中的一種,表示單精度浮點數。C語言規定單精度浮點型在記憶體佔用4個位元組,精度為7位,取值範圍為:3.4*10^-38 ~3.4*10^38或者-(3.4*10^-38 ~3.4*10^38)。依據IEEE規定 :float在儲存中都分為三個部分:

1、符號位(Sign) : 0代表正,1代表為負

2、指數位(Exponent):用於儲存科學計數法中的指數資料,並且採用移位儲存

3、尾數部分(Mantissa):尾數部分

其中float和double的儲存方式如下圖所示:

資料型別 符號位 指數 尾數
float 1bit 8bit 23bit
double 1bit 11bit 52bit

float在記憶體中的儲存規則

計算機只能識別二進位制,所以float型別在記憶體中也是通過二進位制儲存的。因此float型別需要轉換為二進位制形態,轉換步驟如下:

1)先將這個實數的絕對值化為二進位制格式。

2)將這個二進位制格式實數的小數點左移或右移n位,直到小數點移動到第一個有效數字的右邊。

3)從小數點右邊第一位開始數出二十三位數字放入第22到第0位。

4)如果實數是正的,則在第31位放入“0”,否則放入“1”。

5)如果n 是左移得到的,說明指數是正的,第30位放入“1”。如果n是右移得到的或n=0,則第30位放入“0”。

6)如果n是左移得到的,則將n減去1後化為二進位制,並在左邊加“0”補足七位,放入第29到第23位。如果n是右移得到的或n=0,則將n化為二進位制後在左邊加“0”補足七位,再各位求反,再放入第29到第23位

注意:將絕對值轉換為二進位制之後,小數點左移n位,n即為該數的指數,指數為階碼為n+127。這其實就是第5和6步驟運算得到的結果。

示例:

通過上述轉換規則計算兩個浮點數的二進位制。數字 13.6 和 資料 1.3

符號位比較簡單,儲存的是正數那麼符號數就是0。如果是負數,則為1。

下面以13.6為例說明指數與尾數的表示方法。首先,我們取出13.6的整數部為13。對其使用短除法(對該數除以2,直至不能再除的一種方法)結果如下:

將各餘數自下而上排列,則得到了13的二進位制表示。之後,取出13.6的小數部分為0.6對其每次乘2取出整數留下小數,直至得到1。結果如下:

這樣我們就得到了13.6的二進位制表示。為1101.100110011001....... 之後我們需將小數點移動至整數部只有一位。移動後得到1.101100110011001.......。在此我們將小數點移動了三位。因而三即是該數的指數。而階碼則為指數+127(加127是C語言的內在邏輯,在此我們並不深究。)因而我們得到了13.6的指數,為130。二進位制表示10000010。最後的尾數,將原先得到的無線循壞的二進位制取前23位即可,如果第24位為0直接捨棄,如果第24為1,則第23位加1。(float型別的尾數有23位,對於沒有迴圈的數字,在後補齊0即可)。因此我們得到13.6的儲存資料:

0 1000 0010 101 1001 1001 1001 1001 1010 1001......(第24位為1,所以尾數加1變成10,其他的捨棄)

同理得到1.3在記憶體的儲存資料如下:

0 0111 1111 010 0110 0110 0110 0110 0110 0110......(第24為0,後邊的直接捨棄)

接下來通過C程式除錯,來檢視浮點數在記憶體中的儲存形式:

#include <stdio.h>
int main()
{
    float a = 13.6;
    printf("%p\n", &a);
    printf("%.9f\n", a);
    return 0;
}

對上述程式進行除錯,根據打印出變數a的地址在記憶體中找出該浮點數的儲存形式:

可以看出變數a在記憶體中的儲存形式為 41 59 99 9a。(小端儲存:較高的有效位元組存放在較高的儲存器地址,較低的有效位元組存放在較低的儲存器地址)將上述十六進位制轉換為二進位制0100 0001010110011001100110011010 。該結果與上文中我們計算出的資料一致。對於數字 1.3 使用同樣的方式驗證。

從記憶體讀取float型別

從上文已經知道了浮點型在記憶體中如何儲存,那是如何讀取呢,讀取之後為什麼會出現精度丟失的現象。本小節來演示下float型別的讀取。規則如下:

1)將第22位到第0位的二進位制數寫出來,在最左邊補一位“1”,得到二十四位有效數字。將小數點點在最左邊那個“1”的右邊。

2)取出第29到第23位所表示的值n。當30位是“0”時將n各位求反。當30位是“1”時將n增1。

3)將小數點左移n位(當30位是“0”時)或右移n位(當30位是“1”時),得到一個二進位制表示的實數。

4)將這個二進位制實數化為十進位制,並根據第31位是“0”還是“1”加上正號或負號即可

注意:讀取和儲存是相反的過程,階碼也可以通過當前值 -127 來進行轉換。

示例:

從上文中,我們已經從記憶體中獲取到 13.6 的儲存形式為0100 0001 0101 1001 1001 1001 1001 1010 現在通過步驟進行該資料的讀取:

1、獲取尾數得到24位有效數字 1.101 1001 1001 1001 1001 1010

2、00 0001 0 值為2,第31位是1,所以將n+1。得到n為00 0001 1

3、將小數點左移3位得到二進位制實數1101. 1001 1001 1001 1001 1010

5、將小數點前後分別計算十進位制數字,1101轉換為十進位制為13。0. 1001 1001 1001 1001 1010 轉為10進位制數字如下所示:

得到最終實數為 13.6000003814697266。

所以當獲取數字時,如果小數位的有效數字小於7位時,該數字是精確的,超過7位資料就出現了不精確位,所以說浮點型是不精確的資料型別。

同樣的辦法,我們可以獲取到實數 1.3 經過轉換後得到的實數為 1.2999999523162842

我們通過這兩個例子可以得出結論,當讀取資料時,根據需要獲取的精度,之後的資料進行四捨五入之後捨棄。

結論:

經過對程式的除錯,我們已經驗證了浮點型不精確的原因以及float有效精度為6位。所以在使用浮點型時應特別注意精度丟失現象,同樣的double型別有效精度為15位。計算方式和float類似,有興趣的同學可以自行驗證。