引入
首先来看一下以下这段代码:
int main()
{
int n = 1;
float* pf = (float*)&n;
printf("%d\n", n);
printf("%f\n", *pf);
*pf = 1.0;
printf("%d\n", n);
printf("%f\n", *pf);
return 0;
}
这里大多数人可能会认为会输出四个1,而事实真的是这样吗?
可以观察到中间的两个数与我们现象有这很大的差异,第一个与最后一个数与我们想象的一样,由此我们可以得到一个结论:整形与浮点型在内存中的存储的方式是不一样的
一、浮点数在内存中的存储
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点型数 V 可以表示成下面的形式:
V = (-1) ^ S * M + 2^E
S表示符号位,当s = 0,V为正数,当s = 1,V为负数。
阶码部分(E)(指数部分),2^E(表示指数位)
M表示有效数字,大于等于1,小于2, 浮点数的精度就是由尾数来决定的
举例:
十进制的 2.5 ,二进制表示为 10.1 ,写成科学计术法为 1.01 * 2^1
按照 V = (-1) ^ S * M + 2^E 的格式,S = 0,M = 1.01,E = 1,写为 (-1) ^ 0 * 1.01 + 2^1
十进制的 5.5 ,二进制表示为 101.1 ,写成科学计术法为 1.011 * 2^2
按照 V = (-1) ^ S * M + 2^E 的格式,S = 0,M = 1.011,E = 2,写为 (-1) ^ 0 * 1.011 + 2^2
十进制的 -7.5 ,二进制表示为 -111.1 ,写成科学计术法为 1.111 * 2^2
按照 V = (-1) ^ S * M + 2^E 的格式,S = 1,M = 1.111,E = 2,写为 (-1) ^ 1 * 1.111 + 2^2
国际标准IEEE(电气和电子工程协会)754规定:
对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M
补充:
我们要明确一点浮点数在内存中是无法精确保存的
int main()
{
//我们要明确一点浮点数在内存中是无法精确保存的
if (0.1 + 0.2 == 0.3)
{
printf("1");
}
else
{
printf("2");
}
//输出2
return 0;
}
二、浮点数的存入
国际标准IEEE(电气和电子工程协会) 754 对有效数字M和指数E,还有⼀些特别规定:
数字M,在计算机内部保存M时,默认这个数的第⼀位总是1,因此可以被舍去,只保存后⾯的小数部分
⽐如保存1.01的时候,只保存01,等到读取的时候,再把第⼀位的1加上去。这样做的⽬的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第⼀位的1舍去以后,等于可以保存24位有效数字
指数E 规定E为⼀个⽆符号整数,这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047
我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存⼊内存时E的真实值必须再加上⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023
比如 (-1) ^ 1 * 1.111 + 2^2 的E是2,所以保存成32位浮点数时,必须保存成2+127=129,即10000001,保存成64位浮点数时,必须保存成2+1023=1025,即010000000001
int main()
{
float num = 5.5;
int a = 0;
//十进制的 5.5 ,二进制表示为 101.1 ,写成科学计术法为 1.011 * 2 ^ 2
//按照 V = (-1) ^ S * M + 2 ^ E 的格式,S = 0,M = 1.011,E = 2,写为(-1) ^ 0 * 1.011 + 2 ^ 2
//内存中的存储为:0 10000001 01100000000000000000000
//转换为十六进制:0x40 b0 00 00
return 0;
}
三、浮点数的读取
指数E从内存中取出还可以再分成三种情况:
1、E不全为0或不全为1(与存入一样)
浮点数就采⽤,指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第⼀位的1
int main()
{
float num = 0.5;
int a = 0;
//十进制的 0.5 ,二进制表示为 0.5 ,写成科学计术法为 1.0 * 2 ^ -1
//按照 V = (-1) ^ S * M + 2 ^ E 的格式,S = 0,M = 1.0,E = -1,写为(-1) ^ 0 * 1.0 + 2 ^ -1
//阶码为 -1+127(中间值)=126,表⽰为 01111110
//尾数1.0去掉整数部分为0,补⻬0到23位
//内存中的存储为:0 01111110 00000000000000000000000
//转换为十六进制:0x3f 00 00 00
return 0;
}
2、E全为0
浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,⽽是还原为0.xxxxxx的⼩数。这样做是为了表⽰±0,无限接近于0的很⼩的数字
0 00000000 00100000000000000000000
3、E全为1
如果有效数字M全为1,表⽰±⽆穷⼤(正负取决于符号位s)
0 11111111 001000000000000000000000
题目解析
现在我们回到上面的习题
1、第二个打印为什么不是1.0,而是0.0?
在运行第二个打印时1是以整数的形式,存放到内存中的,得到的二进制序列为:
0000 0000 0000 0000 0000 0000 0000 0001
⾸先,将 1 的⼆进制序列按照浮点数的形式拆分,得到第⼀位符号位s=0,后⾯8位的指数E=00000000 ,最后23位的有效数字M=000 0000 0000 0000 0000 0001
由于指数E全为0,所以符合E为全0的情况。因此,浮点数V就写成:
V=(-1)^0 × 0.00000000000000000000001×2^(-126)=1.1×2^(-149)
显然,V是⼀个很⼩的接近于0的正数,所以⽤⼗进制⼩数表⽰就是0.000000
2、第三个打印为什么不是1.0,而是一个很大的数?
在运行第三个打印时1是以浮点数的形式,存放到内存中的,得到的二进制序列为:
0011 1111 1000 0000 0000 0000 0000 0000
十进制的 1.0 ,二进制表示为 1.0 ,写成科学计术法为 1.0 * 2^0
按照 V = (-1) ^ S * M + 2^E 的格式,S = 0,M = 1.0,E = 0,写为 (-1) ^ 0 * 1.0 + 2^0
阶码为 0+127(中间值)=127,表⽰为 01111111 ,尾数1.0去掉整数部分为0,补⻬0到23位
将 0011 1111 1000 0000 0000 0000 0000 0000 二进制序列转换为十进制就是我们在屏幕上打印的数字
标签:存储,1.0,有效数字,浮点数,二进制,内存,0000 From: https://blog.csdn.net/LVZHUO_2022/article/details/142568069