🚀write in front🚀
🔎大家好,我是gugugu。希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
🆔本文由 gugugu 原创 CSDN首发🐒 如需转载还请通知⚠
📝个人主页:gugugu---精品博客
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
📣系列专栏:gugugu的精品博客
✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本📩
前言
整数在内存中以二进制补码的形式存储,这个相信大家都很熟悉,那么浮点数又是怎么在内存中存储的呢?这篇博客将给大家详细讲解。
一、一道有意思的题目
c
#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;
}
首先我们从这个题目引入
仔细去思考一下,这个题目将会输出什么?
答案还是比较奇怪的
为什么 会输出0.000000
又为什么会输出1091567616这么大的数字呢?
在学习玩本章博客之后,就能够明白这道题目。
二、浮点数的存储
1、存储规则
根据国际标准IEEE(电气和电子工程协会) 754,任意⼀个⼆进制浮点数V可以表示成下面的形式:
V = (−1) ^S^ * M * 2^E^
• (−1)^S^ 表⽰符号位,当S=0,V为正数;当S=1,V为负数
• M 表示有效数字,M是大于等于1,小于2的
• 2^E^ 表示指数位
看着是不是很懵圈?
没关系,博主先举几个例子
首先先说5.5
5.5 = 101.101?
- 是不是很多小伙伴会这么写? 哈哈,我猜对了吧,实际上这么写是错误的,
- 因为2进制每一位都有权重 小数点后面第一位的权重是2^-1^ = 0.5
- 所以 , 5.5=101.1
- 又M大于等于1,小于2, 所以101.1=1.011 * 10^2^?
- 又错了, 注意,这里是二进制浮点数,不是10进制
- 所以 101.1=1.011 * 2^2^
- S呢?S怎么处理
- 因为5.5是整数,所以S=0
至此 5.5 = (-1)^0^ * 1.011 * 2^2^
所有的浮点数都可以这么写,各位小伙伴可以自己举几个例子试试吧,当然,别太复杂,否则自己会写吐的。
2、在内存中的占位
IEEE754标准还规定
对于32位的浮点数(float),最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
如下图
对于64位的浮点数(double),最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M
如下图
三、浮点数在内存中存储的具体实现
1、对于S
因为-1的0次方等于1,即正数,-1的1次方等于-1,即负数
所以S中只用存储0或者1就可以完成符号的区分。
2、对于M
因为M大于等于1,小于2,所以M都是一点几,
既然都相同,那就可以不用去存储了,直接去存储小数点后面的数字就可以了。
这样做有什么好处呢???
很显然,这样子做就可以省下储存1的那个比特,可以多储存一个二进制位。
从而提高了浮点数的精度。
3、对于E
首先规定了E为无符号整形。
因为可能出现负指数,所以IEEE754标准中提到一个词------中间数,
指数加上这个中间数,就变成了非负整数。
- 对于32位,E的范围是0~255,中间数就是128
- 对于64位,E的范围是0~2047,中间数就是1023
所以,前面提到的5.5,在内存中存储的就是
S=0, E=2+127=129=10000001,M=011,因为未占满,所以后面全部补0,所以
M=01100000000000000000000
四、浮点数读取的过程的具体实现
浮点数读取有三种情况
a、第一种情况
当E中既有0又有1时,这是,就是正常情况,根据存储的过程反向操作就行。
b、第二种情况
当E中全部都是0的时候,
E的真实值会分成非常小,这个时候,这个浮点数也就是会非常非常小,非常接近于0。
编译器就会将E=1-127或者1-1023,注意是1减,而不是0减。
然后M不会在小数点前面重新加上1.了,而是直接变成0.xxxxxx
通过这些操作来表示0或者一个十分接近0的浮点数。
c、第三种情况
当E中全部都是1的时候,
这时,即使M中的数字为0时,也表示正无穷或者负无穷,即一个超级大或者超级小的数字。
好啦,到这里,浮点数的存储规则讲解的差不多了,现在,我们可以来分析在最开始提出的那个题目了。
五、解决题目
c
#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。
注意这里是按照整形去存储的,所以,内存空间中存储的是(以小端机器16进制来写)09 00 00 00
所以打印n 的时候是9 - 接着将n的地址强制类型转换成一个浮点数指针,赋值给了pFloat
先写成二进制吧
0000 1001 0000 0000 0000 0000 0000 0000
此时,按照浮点数的读取规则,E的真实值是18-127=-109,M=1.0
所以pFloat=1.0* 2^-109^,非常非常小,且按照%f去打印,%f只打印6位小数,很显然,这个数的前六个小数位全部是0,所以,最终打印的是0.000000 - 接着,通过指针指向n的空间,将内存存储的9以浮点数的形式改写成了9.0
9.0以浮点数的存储方式,存储的是(-1)^0^ * 1.001 * 2^3^
所以S=0,E=3+127=130,M=00100000000000000000000
所以32位二进制里面存储的应该是
01000001000100000000000000000000
以%d 十进制整形的方式读取,答案就是
1091567616
与答案相同
- 最后以浮点数的方式打印9.000000
所以四个答案都得到了很好的解释。
okok,今天第二更奉上,关于浮点数的存储是C语言里面的一处比较重要的底层理解,希望大家能够学到不少东西吧。
!!!!!!!!!!!!!!!!!求关注!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!蹲个一键三连!!!!!!!!!!!!!!!