C語言浮點型數(shù)據(jù)在內(nèi)存中的存儲方式詳解
前言
深刻了解浮點型數(shù)據(jù)在內(nèi)存中的存儲方式,是在修煉內(nèi)功,讓后續(xù)的學(xué)習(xí)更深刻,更容易發(fā)現(xiàn)編程過程中的問題并解決問題,繼續(xù)帶鐵汁們學(xué)一波干貨~沖!
一、思考一下
咱們先上一盤開胃菜,試試看叭
#include<stdio.h> int main() { int n = 9; float* pFloat = (float*)&n; printf("n的值為:%d\n", n); printf("*pFloat的值為:%f\n", *pFloat); *pFloat = 9.0; printf("num的值為:%d\n", n); printf("*pFloat的值為:%f\n", *pFloat); return 0; }
路飛:請問打印出來都是什么結(jié)果呢?貝吉塔:簡單,喏
路飛:哈哈~雖然很符合直觀想法,但是錯啦錯啦,喏
貝吉塔:蛤??!這么奇怪的結(jié)果
二、浮點數(shù)存儲規(guī)則
既然上述結(jié)果跟我們所理解的整型數(shù)據(jù)存儲方式的結(jié)果不同,這就說明浮點型數(shù)據(jù)在內(nèi)存中的存儲方式是不一樣滴~
2.1 浮點數(shù)表示方法的規(guī)定
詳細(xì)解讀:
根據(jù)國際標(biāo)準(zhǔn)IEEE(電氣和電子工程協(xié)會)754,任意一個二進(jìn)制浮點數(shù)V
可以表示成下面的形式:
(-1)^S * M * 2^E
(-1)^S
表示符號位。當(dāng)S=0,V為正數(shù);當(dāng)S=1,V為負(fù)數(shù)M
表示有效數(shù)字,大于等于1,小于22^E
表示指數(shù)位
舉例來說:
十進(jìn)制的5.5
,寫成二進(jìn)制是101.1
,相當(dāng)于(1)^0 * 1.011 * 2^2
,此時
aiphabet | Value |
---|---|
S | 0 |
M | 1.011 |
E | 2 |
此時會有鐵汁有疑問,為什么5.5
的二進(jìn)制是101.1
??不應(yīng)該是101.101
嗎?
我們看看下圖,假如是101.101
,那么轉(zhuǎn)換成十進(jìn)制就是5.625
,因為二進(jìn)制每一位的權(quán)重都不同,不能想當(dāng)然
假如說給的十進(jìn)制是5.3
,那么這個0.3
是不能精確表示的,所以說浮點數(shù)容易丟失精度
2.2 浮點數(shù)的存儲
2.2.1 IEEE 754規(guī)定
對于32
位的浮點數(shù)(float
),最高的1
位是符號位S
,接著的8
位是指數(shù)E
,剩下的23
位是有效數(shù)字M
對于64
位的浮點數(shù)(double
),最高的1
位是符號位S
,接著的11
位是指數(shù)E
,剩下的52
位是有效數(shù)字M
2.2.2 IEEE 754對有效數(shù)字M,還有一些特別規(guī)定
前面說過,1≤M<2
,也就是說,M
可以攜程1.xxxxxx
的形式。其中xxxxxx
表示小數(shù)部分
IEEE 754規(guī)定,在計算機(jī)內(nèi)部保存M時,默認(rèn)這個數(shù)的第一位總是1
,因此可以被舍去,只保存后面的xxxxxx
。比如保存1.01
時,只保存01
,等到讀取的時候再放回去,這樣做的目的是節(jié)省空間
2.2.3 IEEE 754對指數(shù)E,還有一些特別規(guī)定
- 首先,E為一個無符號整數(shù)(unsigned int),這意味著,如果E為8位,它的取值范圍為0-255;如果E為11位,它的取值范圍為0-2047。
- 但是我們知道,科學(xué)計數(shù)法中的E是可以出現(xiàn)負(fù)數(shù)的,所以IEEE 754規(guī)定,存入內(nèi)存時E的真實值必須再加上一個中間數(shù)
- 對于8位的E,中間數(shù)取127;對于11位的E,中間數(shù)取1023。
- 比如2^10的E是10,所以保存成32位浮點數(shù)時,必須保存成10+127=137,即為10001001
2.2.4 實操一下看看是怎樣存儲的
int main() { float f = 5.5f; //(-1)^0 * 1.011 * 2^2 //S = 0 //M = 1.011 //E = 2 //這樣存: 0 10000001 01100000000000000000000 //也就是: 0100 0000 1011 0000 0000 0000 0000 0000 //十六進(jìn)制表示: 40 b0 00 00 return 0; }
5.5
按float
存儲,如圖所示
轉(zhuǎn)換成十六進(jìn)制就是40 b0 00 00
,咱們調(diào)試看看叭~因為VS是小端字節(jié)序存儲,所以地址由低到高看到的是00 00 b0 40
2.3 浮點數(shù)從內(nèi)存中取出
2.3.1指數(shù)E從內(nèi)存中取出分三種情況
S
和M
的取出很簡單,原樣返回,但是E
我就得仔細(xì)談?wù)劻?/p>
- E不全為0或不全為1:
這時,把E
減去之前加上去的127
(或1023
),得到真實值,再講有效數(shù)字M前加上第一位的1
,這種情況最簡單,逆著來就是了
- E為全0:
這時真實的E為-127
(或-1023
),太小了,所以規(guī)定浮點數(shù)的指數(shù)E
等于1-127
(或1-1023
)即為真實值,有效數(shù)字M
不再加上第一位的1
,而是還原為0.xxxxxx
的小數(shù)。這樣做是為了表示±0
,以及接近于0
的很小的數(shù)字(這是規(guī)定,大家不要糾結(jié))
- E為全1:
這時真實的E
為128
,太大了,如果有效數(shù)字M
全為0
,表示±無窮大
(正負(fù)取決于符號位S
)
2.3.2 實操一下看看是怎樣取出的
我們回到最開始的代碼,看看能不能解決啦
#include<stdio.h> int main() { int n = 9; float* pFloat = (float*)&n; printf("n的值為:%d\n", n); printf("*pFloat的值為:%f\n", *pFloat); *pFloat = 9.0; printf("num的值為:%d\n", n); printf("*pFloat的值為:%f\n", *pFloat); return 0; }
答案:
- 先分析前兩個輸出:
首先,9
是int
類型的,存儲按整型存儲規(guī)則,就是在內(nèi)存中都是以補(bǔ)碼形式存放的,而正數(shù)的原碼、反碼、補(bǔ)碼都一樣
int | 9 |
---|---|
原碼 | 00000000000000000000000000001001 |
反碼 | 00000000000000000000000000001001 |
補(bǔ)碼 | 00000000000000000000000000001001 |
2.第一個輸出是以%d
(十進(jìn)制整型)輸出,所以輸出結(jié)果確實是9
3. 第二個輸出,pFloat
指針認(rèn)為數(shù)據(jù)是以單精度浮點數(shù)類型存儲的,所以解應(yīng)用的時候也是這么做的。此時pFloat
發(fā)現(xiàn)E
全為0
,按照上述規(guī)則,9
取出來就變成0.000000000000000000001001 * 2^-126
,即使不管后面的指數(shù)!有效數(shù)字都已經(jīng)非常小了!所以打印出來小數(shù)點后6
位看到的是0.000000
然后,分析后兩個輸出:
首先,9.0
是float
類型的,存儲按浮點數(shù)存儲規(guī)則,即為(-1)^0 * 1.001 * 2^3
float | 9.0 |
---|---|
S | 0 |
M | 1.001 |
E | 3 |
存到內(nèi)存里就是:
于是第三個輸出,%d
把它當(dāng)整型輸出,那么在整型眼里,直接把這32
位直接轉(zhuǎn)成十進(jìn)制輸出了,就造成了輸出結(jié)果為1091567616
而第四個輸出就是按float
類型輸出的,所以結(jié)果就是9.000000
三、總結(jié)
- 內(nèi)存可以認(rèn)為都是一堆同樣的小格子
- 但是整型和浮點型數(shù)據(jù)在內(nèi)存中的存儲方式是不一樣的,哪怕都是一樣的內(nèi)存格子,卻有各自的規(guī)則限制著(看起來都是放格子里沒有差別)
- 所以輸出的時候,也有取出的規(guī)則,不然都一樣了
到此這篇關(guān)于C語言浮點型數(shù)據(jù)在內(nèi)存中的存儲方式的文章就介紹到這了,更多相關(guān)C語言浮點型數(shù)據(jù)在內(nèi)存的存儲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++設(shè)計模式之工廠方法模式的實現(xiàn)及優(yōu)缺點
工廠方法模式是一個創(chuàng)建型設(shè)計模式,通過定義一個創(chuàng)建對象的接口,讓其子類決定實例化哪一個工廠類,這篇文章主要給大家介紹了關(guān)于C++設(shè)計模式之工廠方法模式的實現(xiàn)及優(yōu)缺點,需要的朋友可以參考下2021-06-06深入講解C++數(shù)據(jù)類型轉(zhuǎn)換的相關(guān)函數(shù)的知識
這篇文章主要介紹了深入講解C++數(shù)據(jù)類型轉(zhuǎn)換的相關(guān)函數(shù)的知識,包括類型轉(zhuǎn)換運算符函數(shù)等內(nèi)容,需要的朋友可以參考下2015-09-09C語言數(shù)據(jù)結(jié)構(gòu) 雙向鏈表的建立與基本操作
這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu) 雙向鏈表的建立與基本操作的相關(guān)資料,需要的朋友可以參考下2017-03-03C++中與輸入相關(guān)的istream類成員函數(shù)簡介
這篇文章主要介紹了C++中與輸入相關(guān)的istream類成員函數(shù)簡介,包括eof函數(shù)和peek函數(shù)以及putback函數(shù)還有ignore函數(shù),需要的朋友可以參考下2015-09-09