C语言浮点型数据在内存中的存储方式!

直接来吧!
常见的整形有int,unsigned int,signed int等,但在内存中存储时,char类型也可以归为整形家族。
因为char类型是以ASCII码的形式存储在内存当中的,而ASCII码是整形。
整形数据在内存中是以二进制的补码形式来存储的,但详细并不在该篇中,今天我们来说一下浮点型在内存中的存储方式。
在C语言中,形如float,double等数据类型为浮点型也叫实型,而整形和浮点型在内存中的存储方式是不一样的。

首先,我们要知道,数据在内存中是以二进制的形式存储的。
根据标准(IEEE 754)定义,任何一个二进制浮点数X,可以表示为以下形式:
公式:(-1)^S * M * 2^E
其中(-1)^S表示符号位,当S=0,X为正数;当S = 1,X为负数
M表示有效数字;M的取值范围是大于等于1,小于2的数;1 ≦ M < 2。
2^E表示指数位

标准规定:
32位(单精度浮点):最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数M。
64位(双精度浮点):最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数M。

针对M和E,标准还有特别规定:
先来说有效数M:
前面说过,M∈[1,2),也就是说,M可以写成1.xxx的形式,其中xxx表示小数部分。
这样就造成了在计算机内部保存M时,默认这个数的第一位总会是1,既然是恒定的数字那为什么还要让他存在着浪费内存空间呢?
所以啊,M在计算机内部保存时,会无视这第一位的1,将后面的xxx存入M中,而在取出时再加上这第一位的1,既不影响浮点数在内存中的存储,也不使得内存空间出现浪费。
再来说指数E:
首先,E是一个无符号整形(unsigned int),这意味着,如果E为8位,它的取值范围就是0~255;如果E为11位,它的取值范围就是0~2047.
我们知道,科学计算法中的E是可以出现负数的,所以标准规定,存入内存中E的真实值必须再加上一个中间数。8位的E,这个中间数为127;11位的E,这个中间数为1023。
比如,2^10,E=10,所以保存成32位浮点数时,必须写成10+127=137,即10001001

eg①:浮点数5.0在内存中的存储方式?
已知5.0为正数,S=0
5.0的二进制为101.0,将101.0小数点左移,直至小数点左边的数,符合M的取值范围,得到:M = 1.01
从101.0到1.010,小数点向左移了2位,所以E=2
已知未知数,代入公式:(-1)^0 * 1.01 * 2^2
接下来就是将数据存入内存:
32位:
S占用1字节,S为0,将0存入内存中;
M占用23字节,M为1.01,按标准将1省去得01,存入内存中的就是0100 0000 0000 0000 0000 000(不够补0,直到凑够23位)
E占用8字节,E=2,127+2=129,129的二进制为:1000 0001
存入内存中的顺序依次为:
S -> E -> M
0 10000001 01000000000000000000000
在内存中的存储形式:0100 0000 1010 0000 0000 0000 0000 0000(4位二进制位 = 1位16进制位)
编译器调试地址:0x 00 00 a0 40(小端字节序。关于大小端字节序,该篇并无讲解)
64位:
S占用1字节,S为0,将0存入内存中;
M占用52字节,M为1.01,按标准将1省去得01,存入内存中的就是0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000;
E占用11字节,E=2,1023+2=1025,1025的二进制为:100 0000 0001。
存入内存中的顺序依次为:
S -> E -> M
0 10000000001 0100000000000000000000000000000000000000000000000000
在内存中的存储形式:0100 0000 0001 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000(4位二进制位 = 1位16进制位)
编译器调试地址:0x 00 00 00 00 00 00 14 40 (小端字节序。关于大小端字节序,该篇并无讲解)

eg②:浮点数48.5在内存中的存储方式?
我们知道48的二进制怎么计算,那0.5的二进制呢?
不要急,一步一步来,首先48.5为正数,S=0。
小数二进制详解:
解法:乘2取余法
先看0.5的二进制:
0.5*2 = 1 余 0
0.5 的二进制就是 0.1
0.5太简单,看不出来什么,那么我们再来看0.6875的二进制:
0.6875 * 2 = 1 余 0.375
0.375 * 2 = 0 余 0.75
0.75 * 2 = 1 余 0.5
0.5 * 2 = 1 余 0
将所求整数由上而下(从上到下)依次排列得到1011,由于我们求的是小数的二进制,所以要在1011前加上‘ 0. ’,最后得到‘ 0.1011 ’。
知道了方法,掌握了方法,就会进行更深入的研究,我们再来看:
说明:
(须知知识!求一个数的负数次方公式:a^-x=1/a^x)
二进制的1011,通过1*2^0 + 1*2^1 + 0*2^2 + 1*2^3 = 1+2+0+8 = 11
二进制的0.1011,通过
1*2^-1 + 0*2^-2 + 1*2^-3 + 1*2^-4
= 1*(1/2^1) + 0*(1/2^2) + 1*(1/2^3) + 1*(1/2^4)
= 1/2 + 0 + 1/8 + 1/16
= 0.5 + 0 + 0.125 + 0.0625
= 0.6875
已知48的二进制为110000 ,0.5的二进制为0.1,所以48.5的二进制就是110000.1
将小数点左移,直至符合M的取值范围M∈[1,2),M = 1.100001,共左移5位,E = 5。
已知未知数,代入公式:(-1)^0 * 1.100001 * 2^5
接下来就是将数据存入内存:
32位:
S占用1字节,S为0,将0存入内存中;
M占用23字节,M为1.100001,按标准将1省去得100001,存入内存中的就是1000 0100 0000 0000 0000 000;
E占用8字节,E=5,127+5=132,132的二进制为:1000 0100。
存入内存中的顺序依次为:
S -> E -> M
0 10000100 10000100000000000000000
在内存中的存储形式:0100 0010 0100 0010 0000 0000 0000 0000(4位二进制位 = 1位16进制位)
编译器调试地址:0x 00 00 42 42(小端字节序)
64位:
S占用1字节,S为0,将0存入内存中;
M占用52字节,M为1.100001,按标准将1省去得100001,存入内存中的就是1000 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000;
E占用11字节,E=2,1023+5=1028,1028的二进制为:100 0000 0100。
存入内存中的顺序依次为:
S -> E -> M
0 10000000100 1000010000000000000000000000000000000000000000000000
在内存中的存储形式:0100 0000 0100 1000 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000(4位二进制位 = 1位16进制位)
编译器调试地址:0x 00 00 00 00 00 40 48 40(小端字节序)

eg③:浮点数0.5在内存中的存储方式?
0.5为正数,二进制为0.1,M∈[1,2),所以小数点要像右移一位,M=1,E=-1;
(-1)^0 * 1 * 2^-1
接下来就是将数据存入内存:
32位:
S占用1字节,S为0,将0存入内存中;
M占用23字节,M为1,按标准将1省去没了,内存中补零0000 0000 0000 0000 0000 000;
E占用8字节,E=-1,127-1=126,126的二进制为:0111 1110
存入内存中的顺序依次为:
S -> E -> M
0 01111110 00000000000000000000000
在内存中的存储形式:0011 1111 0000 0000 0000 0000 0000 0000(4位二进制位 = 1位16进制位)
编译器调试地址:0x 00 00 00 3f(小端字节序)
64位:
S占用1字节,S为0,将0存入内存中;
M占用52字节,M为1,按标准将1省去没了,存入内存中的就是0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000;
E占用11字节,E=2,1023-1=1022,1022的二进制为:011 1111 1110。
存入内存中的顺序依次为:
S -> E -> M
0 01111111110 0000000000000000000000000000000000000000000000000000
在内存中的存储形式:0011 1111 1110 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000(4位二进制位 = 1位16进制位)
编译器调试地址:0x 00 00 00 00 00 00 e0 3f(小端字节序)

说完存储,我们再来看取出:
将二进制数据以浮点型取出,需要将所做修改尽数还原,最主要的是E:
指数E从内存中取出分成三种情况:
1.E不全为0或不全为1(该情况为正常情况。怎么来的,就怎么回去)
首先,E需要转为十进制并减去(32位:127 或 64位:1023),得到真实值,再将M的1补回,反推公式,得到浮点数。
(2,3较特殊且不常见,如果掌握上述知识,完全可根据自身条件进行推导)
2.E全为0(无穷小)
此时,浮点数的指数E=1-127或1-1023即为真实值,有效数字M不再补回第一位的1,而是还原为0.xxx的小数。
这样做是为了表示±0,以及接近于0的很小的数字。
(0.0000000000000000000000000000001如果真的有一天需要你用手推这种数,那么可能是你穿越了)
3.E全为1(无穷大)
此时,如果有效数字M全为0,表示±∞(正负取决于符号位S)

eg④:32位:0100 0000 1010 0000 0000 0000 0000 0000以浮点数取出赋给a,a=_?
用已知知识反推,将二进制串分割成易读的:0 10000001 01000000000000000000000
M补1 = 1.01000000000000000000000 = 1.01
E = 1000 0001 = 129 -> 129-127 = 2
反推公式:(-1)^S * M * 2^E = (-1)^0 * 1.01 * 2^2(可有可无)
答:M = 1.01 -> 右移2位得 101.0 转回十进制 5.0
感谢大家能看到这里!