次我们讲到了整数在内存中的存储,那么是否大家有没有跟我有同样的疑问————浮点数在内存中的存储是否跟整数是一样的存储方式呢??
话不多说,我们直接看下面代码
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 9.000000 9 9.000000一样呢
我们往下看:
- 我们思考一下,当我们以整数的形式放进去,以整数的形式拿出来就得到了我们的第一个答案9,但当我们以整数的形式放进去,以浮点数的形式拿出来的时候却不是我们想要的答案,这是不是说明在内存中以浮点数的形式取出来和以整数的形式取出来是不一样的,内存中整数的存储和浮点数的存储是有差异的。
- 我们也要理解这个答案是如何产生的,这就要我们去搞懂浮点数在计算机内部的表示方法。
- 根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示为:
- (-1)^S*M*2^E
- (-1)^S表示符号位 当s=0, V为整数, 当s=1,V为负数
- M表示有效数字,大于等于1,小于2
- 2^E表示质数位
- 我们举个例子:十进制的5.0,写成二进制是101.0 ,相当于1.01x2^2。那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。
- 十进制的-5.0,二进制是-101.0,相当于-1.01x2^2。那么,s=1,E=2,M=1.01
IEEE 754对有效数字M和E,有特别规定的。当1<=M<=2时,M可以表示为1.xxxxxxxx的形式,其中xxxxxxxx表示小数部分。
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存xxxxxxxx的部分。比如在保存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。比如,2^10的E是10,所以保存32位浮点数时,必须保存10+127,即10001001。
- E从内存中取出还可以再分成三种情况:
- 第一种情况:E不全为0和1
- 这时,浮点数就采用了下面的规则,即E减去127得到真实值,再将有效数字M前面的1加上去。比如:0.5(1/2)的二进制形式为0.1,由于规定整数部分必须为1,即将小数点后移1位,则为1.0*2(-1),按照规则-1要加上127得到126,表示为01111110,而尾数1,0去掉整数部分0,补齐0到23位00000000 00000000 0000000
- 所以最终的二进制就表示为: 0 01111110 00000000 00000000 0000000
- 第二种情况:E全为1
- 这时,如果有效数字M全为0,表示+-无穷大(正负取决于S)。
- 第三种情况:E全为0
- 这时,浮点数的指数E等于1-127(或1023),得到真实值,有效数字M不再是加上1,而是还原成0.xxxxxxx的小数形式。这样做是为了表示正负0,以及接近0的数字。
- 当然我们也要知道为什么不再加上1,真实值应该为-127,浮点数就可以表示为(-1)^S*2^(-127)*1.xxxxxxxx,这个数接近于0的,这时我们就有了这个规定,有效数字M前也不再加上1了
了解玩IEEE 754的规则后,回到我们最开始的问题,为什么0x00000009,还原成浮点数变成了0.000000了呢??? 首先将0x00000009拆分,得到第一位符号s=0,后面8位的指数E=00000000 ,最后23位的有效数字为000 0000 00000000 00001001
int main() { int n = 9; //9-> 0 0000000 0000 0000 0000 0000 0000 1001 //由于E为全0,所以符合第三种情况。 //因此浮点数可以表示为V= (-1)^0*0.00000000000000000001001*2*(-126)=1.001*2^(-146) //很明显V是一个接近于0的数,所以结果表示为0.000000 float* pFloat = (float*)&n; printf("n的值为:%d\n", n); printf("*pFloat的值为:%f\n", *pFloat); *pFloat = 9.0; //9.0-> 1001.0 可以表示为(-1)^0*1.0010*2^3 所以S=0,M=1.0010 E=3 //我们还原成二进制就是 0 10000010 001 0000 0000 0000 0000 0000 //这个二级制换成十进制就是 1091567616 printf("num的值为:%d\n", n); printf("*pFloat的值为:%f\n", *pFloat); return 0; }
最后我们用计算机验证一下:
最后:希望这篇文章让你对浮点数的存储有新的认识,我也会不定时更新,分享我的学习经历。