首頁 > 軟體

C語言資料的儲存超詳細講解下篇浮點型在記憶體中的存取

2022-04-14 10:01:56

前言

本文接著學習資料的儲存相關的內容,主要學習浮點型數在記憶體中的儲存與取出。

浮點型在記憶體中的儲存

  • 常見的浮點數:3.14159、1E10
  • 浮點數家族包括: float、double、long double 型別
  • 浮點數表示的範圍:float.h中定義

浮點數儲存的例子

int main()
{
	int n = 9;
	float *pFloat = (float*)&n;
	printf("n的值為:%dn",n);
	printf("*pFloat的值為:%fn",*pFloat);
	
	*pFloat = 9.0;
	printf("num的值為:%dn",n);
	printf("*pFloat的值為:%fn",*pFloat);
	return 0;
}

查德一看,輸出結果是: 9 、9.0、9、9.0

執行結果見下圖:

浮點數儲存規則

num 和 *pFloat 在記憶體中明明是同一個數,但是浮點數和整數的解讀結果會差別巨大,要理解這個結果,需要理解浮點數在計算機內部的表示方法。

根據國際標準IEEE(電氣和電子工程協會) 754,任意一個二進位制浮點數V可以表示成下面的形式:

  • (-1)^S * M * 2^E
  • (-1)^s表示符號位,當s=0,V為正數;當s=1,V為負數
  • M表示有效數位,大於等於1,小於2
  • 2^E表示指數位

舉例說明:

  • 十進位制的5.0,寫成二進位制是 101.0 ,相當於 1.01×2^2
  • 按照上面的儲存規則,可以得出s=0,M=1.01,E=2。
  • 十進位制的-5.0,寫成二進位制是 -101.0 ,相當於 -1.01×2^2
  • 按照上面的儲存規則,s=1,M=1.01,E=2

IEEE 754規定

對於32位元的浮點數,最高的1位是符號位s,接著的8位元是指數E,剩下的23位為有效數位M

對於64位元的浮點數,最高的1位是符號位S,接著的11位是指數E,剩下的52位為有效數位M

IEEE 754對有效數位M的特別規定

  • 規定中1≤M<2 ,也就是說,M可以寫成 1.xxxxxx 的形式,其中xxxxxx表示小數部分
  • 在計算機內部儲存M時,預設這個數的第一位總是1,因此可以被捨去,只儲存後面的xxxxxx部分
  • 例如儲存1.01的時候,只儲存01,等到讀取的時候,再把第一位的1加上去。這樣做的目的,是節省1位有效數位
  • 以32位元浮點數為例,留給M只有23位,將第一位的1捨去以後,等於可以儲存24位元有效數位

IEEE 754對指數E的特別規定

存入記憶體是E的規定

E為一個無符號整數(unsigned int)

  • 如果E為8位元,它的取值範圍為0-255
  • 如果E為11位,它的取值範圍為0~2047

但是,科學計數法中的E是可以出現負數的,所以IEEE 754規定,存入記憶體時E的真實值必須再加上一個中間數:

  • 對於8位元的E,這個中間數是127
  • 對於11位的E,這個中間數是1023
  • 例如,2^10的E是10,所以儲存成32位元浮點數時,必須儲存成10+127=137,即10001001

從記憶體取出時E的規定

1、E不全為0或不全為1

  • 浮點數就採用下面的規則表示,即指數E的計算值減去127(或1023),得到真實值,再將有效數位M前加上第一位的1
  • 例如:0.5(1/2)的二進位制形式為0.1,由於規定正數部分必須為1,即將小數點右移1位,則為1.0*2^(-1),其階碼為-1+127=126,表示為01111110
  • 尾數1.0去掉整數部分為0,補齊0到23位00000000000000000000000,則其二進位制表示形式為0 01111110 00000000000000000000000

2、E全為0

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

3、E全為1

如果有效數位M全為0,表示±無窮大(正負取決於符號位s)

舉例 1

int main()
{
	float f = 5.5; //浮點數
	101.1 二進位制表示
	(-1)^0 * 1.011* 2^2 IEEE 745規定
	s=0 //代表正數
	E=2  //代表指數,左移2位  ,儲存是時要+127 =129
	M=1.011 //有效數位
	0 10000001 01100000000000000000000
	0100 0000 1011 0000 0000 0000 0000 0000
	0x40 b0 00 00 小端儲存
}

由上面分析可知,浮點數5.5在記憶體的儲存形式見下圖:

偵錯程式發現結果與分析過程一致,並且是小端儲存。

舉例 2

int main()
{
	float f = 0.5; 浮點數
	0.1 二進位制表示
	(-1)^0 * 1.0*2^-1 IEEE 745規定
	S = 0   代表正數
	M = 1.0  有效數位
	E = -1   代表指數,右移1位  ,儲存是要+127 =126
	0 01111110 00000000000000000000000
	0011 1111 0000 0000 0000 0000 0000 0000
	0x3f 00 00 00
	
	return 0;
}

由上面分析可知,浮點數0.5在記憶體的儲存形式見下圖:

偵錯程式發現結果與分析過程一致,並且是小端儲存。

舉例 3

下面對 3.1的例子進行講解:

int main()
{
    int n = 9;
    第一步:正數9在記憶體儲存的形式:
    00000000000000000000000000001001
    float *pFloat = (float*)&n;
    第二步:將正數強制轉換位浮點型,認為pfloat指向的內容是浮點數
    儲存在記憶體中的形式
    0 00000000 00000000000000000001001
    0000 0000 0000 0000 0000 1001
    0x00 00 00 09
    s=0
    E= -126  因為E是全為0的特殊情況,取出就是1-127固定的
    M= 0.00000000000000000001001 後面23位都是小數位
    第三步:從記憶體中取出浮點數
    (-1)^0 * 0.00000000000000000001001 * 2^-126 
    結果為極限接近0的非常小的數
    printf("n的值為:%dn",n); 輸出9
    printf("*pFloat的值為:%fn",*pFloat);輸出浮點數0.00000
    
    *pFloat = 9.0;
    第一步:浮點數9.0的二進位制形式:
    1001.0
    (-1)^0 * 1.001 * 2^3
    s=0
    E=3   代表指數,左移3位  ,儲存是要+127 =130
    M=1.001
    第二步:浮點數9.0在記憶體儲存的形式:
    0 10000010 00100000000000000000000
    0100 0001 0001 0000 0000 0000 0000
    0x41 10 00 00
    第三步:%d列印,上面的二補數就是正數的二補數了,三碼合一
    列印原碼: 1,091,567,616
    printf("num的值為:%dn",n);
    printf("*pFloat的值為:%fn",*pFloat); 9.0
    return 0;
}

此時再看結果,就會一目瞭然了:

- 輸出浮點數0.00000,浮點數在記憶體的儲存形式 0x00 00 00 09,小端儲存形式:

輸出正數1,091,567,616,正數數在記憶體的儲存形式 0x41 10 00 00,小端儲存形式:

輸出結果與分析一致:

判斷兩個浮點數是否相等?

兩個浮點數不能直接判斷是否相等,應該判斷他們之間的差值是否在一個給定範圍內,滿足自己的使用要求即可。

int main()
{
	int a = 0;
	if (a == 1)//整數可以直接判斷
	{

	}
	float b = 0.00001;//基本接近0,但不是0
	if (b==0.0)//不能這樣判斷,會出問題
	{

	}
}

總結

資料的儲存相關內容是C語言進階階段的第一個知識點,與整形提升關係密切,還要熟悉變數型別、符號位、型別範圍、原碼反碼二補數、等等,這部分內容更多的牢記變數儲存型別的性質,不能想當然的去考慮結果,每一步的思考都要有依據才行。

要溫故而知新,通過這部分的學習,給自己在以後的程式找錯中,提供了不一樣的思路。

下一篇開始學習指標進階的內容了。(連結直達

到此這篇關於C語言資料的儲存超詳細講解下篇浮點型在記憶體中的存取的文章就介紹到這了,更多相關C語言 浮點型資料的儲存 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com