一、案例引入
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;
}
【打印结果】 :
n的值为: 9
*pFloat的值为: 0.000000
num的值为: 1091567616
*pFloat的值为: 9.000000
从上面的结果我们可以看出浮点数和整数在内存中存储的方式是不同的。我们知道整数在内存中存储遵循原反补
的规则,而浮点数遵循的规则是IEEE754
。现在我么来学习一下国际标准IEEE754。
二、IEEE754标准
- 任何一个浮点数都可以表示成 (-1) ^ S * M * 2 ^ E
- (-1) ^ S用来表示符号位。当S为1时表示负数,S为0时表示正数
- M表示有效数字,注意M大于等于1小于2
- 2 ^ E表示指数位
我们举个例子巩固一下。以5.5为例:
- 首先写下5.5的二进制表达式——101.1(注意不是101.101,小数点后每一位的权重依次为0.5, 0.25,0.125……)
- 用IEE754的标准表示: (-1) ^ 0 * 1.011 * 2 ^ 2
- 可以得出S = 0; M = 1.011;E = 2;
三、S、M、E占用比特位
对于32位的浮点数,最高一位是S,其次8位是E,最后23位为有效数字E
对于64位的浮点数,最高一位是S,其次11位是E,最后54位是有效数字M
四、关于M和E的特殊规定
①关于M的特殊规定
由于M的范围在 1 <= M < 2上,即M第一位必定为1,因此在存储的时候不保留第一位的1,直接从小数点后开始存储。在读取的时候再加上被默认省略的1。
②关于E的特殊规定
由于E为一个无符号整数,32位浮点数E的范围为0 ~ 255,64位浮点数E的范围为0 ~2047。因此如果指数为负数怎么办呢?IEEE754规定在存入一个数的时候首先要加上一个中间值:
- 对于32位浮点数来说,这个中间值为127。如果E为10时,实际存入137
- 对于64位浮点数来说,这个中间值为1023。如果E为10时,实际存入1033
五、E的三种情况
①E不全为0或1
从二进制中读取时E的值减去127(或者1023)得到E的真实值,再还原回有效数字M第一位的1得到M的真实值
例如:以32位浮点数0.75为例
1、写成二进制形式为0.11
2、由于要求第一位必须为1,所以用科学计数法写成 (-1) ^ 0 * 1.1 * 2 ^ (-1)
3、S = 0; E = -1 + 127;M = 1(后面补0至23位)
4、存入二进制中为 0 01111110 10000000000000000000000
用16进制表示为 3f 40 00 00。我们通过监视来验证我们的结果:
从上面我们还可以发现,不管是浮点数还是整数在内存中存储都是遵循大小端
的原则的。
②E全为1
E全为1时减去127(或者1023)得到的真正值为128,这是一个非常大的数字,根据前面符号位的正负来表示正负无穷
③E全为0
E全为0时减去127(或者1023)得到的真实值为-127,这是一个非常小的数字。有效数字不再加上前面的1,相应的E变成-126。这样做是为了表示±0,以及接近0很小的数字。
六、案例回顾
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在内存中的补码为 00000000 00000000 00000000 00001001
- 由于指数E全为0,所以用浮点数表示这是一个非常小的数,所以打印出来就是0.000000
- 浮点数的9是这样表示的:S = 0;E = 3 + 127 = 10000010;M = 001 后面加上20个0补齐23位,所以最终是0 10000010 00100000000000000000000
- 对于上面的二进制序列,整数是怎么理解的:
所以学到这相信大家理解了浮点数在内存中是如何存储的!