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