一.浮点数以什么形式存储在内存中
根据根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V都可以存储为这样的形式:
V=(-1)^S*M*2^E。
(1)(-1)^S表示符号位,当S为0时,V为正数;S为1时是负数。
(2)M表示有效数字,M是大于等于1,小于2的
(3)2^E表示指数位。
举个例子:比如5.0,写成二进制是101.0,相当于1.01*2^2。则S=0,M=1.01,E=2.
比如-5.0,相当于-1.01*2^2。则S=1,M=1.01,E=2.
再比如5.5,二进制是101.1,所以为1.011*2^2.则S=0,M=1.011,E=2.
IEEE 754规定:
对于32位的浮点数(float),最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M.
对于64位的浮点数(double),最高的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M.
这就是关于浮点数在内存中最基础的存储。
二.浮点数存的过程
前面我们说,M我们可以写成1.xxxxx的形式,其中xxxxx是小数部分。当我们保存M的时候,默认这个数的第一位总是1。因此我们可以把这个1舍去,只保存后面的xxxxxx。比如我们存储1.011的时候,只保存011,等到读取的时候,再把第一位的1加上去。这样我们就可以节省出1位有效数字,就像是M本来只能储存23位,但是省去了第一位后,相当于我们最多可以存储24位。
关于E的存储,就有些复杂了,首先它必须是一个无符号的整数。假如E为8位,它的取值范围就是0~255,如果为11,就是0~2047。但是我们知道,这个是指数,科学计数法里是可以出现负数的,所以IEEE 754规定,存入内存的E必须加上一个中间数,对于8位的E,中间数就是127。11位的就是1023.比如,2^10,我们保存成32位时,我们需要保存成137,也就是10001001。
大家注意,float类型是有最大值和最小值的。所以我们的E肯定也是有最大值和最小值的。不过我们不用担心我们加上127或者1023会超出这个范围。
来给大家举个例子
至于为什么是反过来的,可以看我上一篇博客,关于大小端存储的内容。
注意这里我举的例子是5.5在内存中可以精确保存的,但是像是3.14,1.1等等这些数字,是无法再内存中精确保存的。因为我们数据是以二进制存储的,小数点后面是2^-1,2^-2等等,这样的数据对于32位的float是无法精确保存的。
三.浮点数取的过程
1.E不全为1或不全为0
比如0.5的存储
0 01111110 0000000000000000000000
这个最好理解,当我们把存入内存的数据取出来的时候,先把指数E减去127或者1023,得到真实值,再将有效数字M前加上第一位的1(因为我们在存的时候不是把这个省去了吗)。整体的步骤实际上就是把我们存进去的时候完全反了过来。
2.E全为0
0 00000000 00100000000000000000000
这是一种特殊情况,这个时候的E等于1-127或者1-1023这个就是真实值,有效数字M不再加上第一位的1,而是还原成0.xxxxxx的小数。这样做是为了表示±0,以及接近0的很小的数字。
3.E全为1
0 11111111 00100000000000000000000
这个也是一个特殊情况,这里我们有八个1,表示的十进制数是255,大家可以想想,因为我们是加完127之后得到的这个数字,那么真实值就是255-127=128。那么也就是说,这个数字是2^128这是一个天文数字。所以它就表示的是正负无穷大的数字。
四.练习
来看一下这四个输出的是什么结果。
#include<stdio.h>
int main()
{int a = 9;float* pfloat = (float*)&a;printf("a=%d\n", a);printf("*ploat=%f\n", *pfloat);*pfloat = 9.0;printf("a=%d\n", a);printf("*ploat=%f", *pfloat);return 0;
}
这个就要用到我刚才写的那些内容了。
第一个printf就不用多说,输出的肯定是9。
第二个值是几呢?9的二进制是1001,补成32位之后是00000000000000000000000000001001,这里我们把a的地址取出来,因为&a的类型是int*所以这里肯定要强制转换成float*。此时的pfloat指向的就是一个float*类型的a。站在pfloat的角度,它会认为自己指向的是一个float的类型。所以这里的32位就会被理解成是用浮点数存储的方式。也就是
0 00000000 00000000000000000001001
这种情况就是我们上面说的E为全0的特殊情况。E的值就是-126,有效数字M也不再加上第一位的1.所以这里就是(-1)*0.00000000000000000001001*2^-126这个值就直接约等于0了。
第二个打印出来结果:0.000000
第三个的结果我们是把浮点数类型的9.0用%d打印出来,其实只要理解了第二个这一个也不算难了。回想一下浮点数在内存的存储。9.0的二进制是1001.0也就是(-1)^01.001*2^3.S=0,E=3,M=1.001。所以0 10000010 00100000000000000000000这个就是9.0在内存的存储。但是这里打印的是用%d打印,认为内存里的是有符号说,所以这里的最高位0代表的就是正。而正数的原码,反码和补码都是一样的。所以我们就按照这个原码来打印出来。
第三个打印出来的结果:1,091,567,616
这那么最后一个打印出来的这个就是以浮点数的形式打印的,所以就是9.000000
好了,到这里就把数据在内存中的存储就全部讲完了。感谢大家的观看,如有错误,请大家多多指出。