前面跟大家介绍了整数在内存中的存储,这次再向大家介绍下浮点数在内存中的存储。
我们常见的浮点数有3.14,1E10 等等,浮点数家族包括float,double,long double类型。
浮点数的表示范围在头文件 float.h 定义。
1.浮点数的储存
首先,我们要清楚浮点数于整数在内存中的存储方式是完全不一样的。
浮点数在内存的存储方式有一个国际标准。
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V都可以表示成以下形式:
对于64位浮点数(double),最高的一位存储符号位S,接着11位存储指数E,剩下的 52位存储有效数字M。如下图
2.浮点数存的过程
IEEE 754 对有效数字M和指数E,还有一些特别的规定。
前面我们说过,M是大于等于1且小于2的,也就是说M总是写成1.xxxxx的形式,其中xxxxx表示小数部分。IEEE 754 规定 再存储有效数字M的时候只存储其xxxxx小数部分,因为其整数部分被默认为1了。 比如当M=1.01时,我们只将01存储到内存中,等到读取的再将1补上去就行了。这样做的目的是节省一位有效数字位,让其精确度更高。这样我们就清楚了为什么float被称作单精度浮点数,double被称为双精度浮点数了,因为double类型的浮点数在存储时的精确度更高。
至于指数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位浮点数的时候,要加上127,变成137,即10001001。
3.浮点数取的过程
指数E在内存中取出可以分成三种情况。
E不全为0或不全为1
这种情况就属于一种正常情况。我们只要将E减上127或者1023,得到真实值,再将有效数字M前加上1。ru
比如:0.5
0.5的二进制为0.1,由于规定正数部分必须为1,则写成1.0*2^-1。
则E= -1+127=126,表示为01111110,尾数部分要去掉整数部分1,为0,又因为M要存到23位,则补0,则二进制表示形式为: 0 01111110 00000000000000000000000 如下图
这也是其在内存中存储的形式。
上面是0.5存储到内存中的过程。
我们的时候取出来,发现其E不全为0或不全为1,我们将浮点数从内存中取出来的步骤和存进去的步骤相反 E为01111110 ,转换为10进制为 126,再用126-127= -1,然后将M的整数部分1补上去。最终得到 (-1)^0*1.0*2^-1,计算的0.5。
E全为0时
当E全为0时,真实E就是一个非常小的数字了,因为前面我们说过E在存入内存中,要加上一个中间数127或者1023,如果加上这个中间数E在内存中还为0的话,E的真实值为1-127或1-1023了,这时E就是一个非常小的数字了,这时也不会将M的整数部分1不上去了,这时,原来的浮点数就是一个非常逼近0的数字了,一般情况下都会默认等于0。
E全为1
当E全为1时,则表示原数无穷大。
比如算32位浮点数时,真实E为255-127=128。然而2的32次方就已经42亿多了,更别提2的128次方了。
练习题
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,n的值为9,you
写成其二进制形式: 00000000000000000000000000001001
后来又创建了一个float 类型的指针存储了n,这时内存已经将n看作一个浮点数存储在内存中了,
这时内存中默认存的是浮点数了,将原来的二进制看成浮点数的形式,观察发现E全为0,所以当以%f 的形式打印时,会默认为0。
接着分析,
后来有对pfloat指针进行解引用,将9.0赋值给指针 。
由于是直接将9.0存储到float类型中,这时候直接用浮点数存储到内存中的方式。
将9.0转换为二进制:1001.0
在写成科学计数法的形式:1.001*2^3。由此得到 S=0,M=1.001,E=3
由于是计算的是32位浮点数,E要加上127,得到130,130的二进制表示形式位:10000010,然后M要去掉整数部分得001,因为M要存23位,在001后面补20个0。
得到完整形式为0 10000010 00100000000000000000000
这就是此时9.0在内存中的存储形式。
当后面以%d形式打印时,要以有符号整型的形式打印,这时上面的二进制就会被认作是9.0的补码,由于无符号数的原码,反码,补码相同,由上面二进制计算可得应该是一个非常大的数字。
当以%f的的格式化打印时,上面的二进制就是浮点数的存储形式的,原来是9.0,打印时也是9.0
运行代码,如下图