我们知道,对于一个整数,在计算机内存中是以二进制的形式来存储的,但对于一个浮点数尼?假如仍以浮点数的二进制来存储,那小数点的位置如何来固定尼?所以人们就想到可以用二进制的科学计数法来保存,例如:5.5,其二进制数为101.1,则用二进制的科学计数法可以表示为 1.011 * 2^2,这样小数点的位置就是固定的。
任何一个浮点数可以表示为下面的形式(国际标准IEEE 754):
- (-1)^S * M * 2^E
- (-1)^S表示符号位,当S=0时,为正;当S=1时,为负
- M表示有效数字,M>=1且M<2
- 2^E表示指数位
上面的例子中,S=0,M=1.011,E=2。
所以当存储浮点数时,只需要将上述三个数字保存起来,就可以了。
对于但精度浮点数,其存储模型为:
对于双精度浮点数,其存储模式为:
其中,S只可能为0或者1,所以只需用一个bit位即可保存。
对于M:
前面说过,M>=1且M<2,也就是M可以表示为1.xxxxxxxxxxx的形式,其中xxxxxxxxxxx即为小数部分。、
所以在存储M的时候,只需要存储后面的小数部分就可以了,比如上述的1.011,在存储的时候只存储011。等到读取的时候,只需在前面加上1即可。并且这样做,还可以节省一位有效数字。假如把1保存进去,则小数点后面只能保存22位有效数字,如果不把1保存进去,皆可以保存23位有效数字。
但对于E而言,情况比较复杂:
首先,E是一个无符号整数(unsigned int)
这表示,对于单精度浮点数而言,E的取值范围为0到255;对于双精度浮点数而言,E的取值范围为0到2047
但是,在科学计数法中,E是可以取负数的
IEEE 754规定:存入内存时,E的真实值必须加上一个中间数
对于单精度浮点数,中间数为127
对于双精度浮点数,中间数为1023
列入,2^2在保存时,必须保存为2+127,即10000001
E可以分为三种情况:
E不全为0或不全为1
这种情况是比较常规的,按照上面的规则就可以了
例如 5.5,则其二进制形式为:
0 10000010 01100000000000000000000
E全为0
此时,浮点数的指数E等于1-127(或1-1023)即为真实值
有效数字M不再加上第一位的1,而是还原成0.xxxxxxxx的形式。
此时是一个接近于0的很小的数字
E全为1
此时,如果有效数字M全为0,表示+无穷或-无穷(正负取决于S)。
下面是我遇见的一个关于浮点数存储的题:
#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("n = %d\n", n);
printf("*pFloat = %f\n", *pFloat);
return 0;
}
最后的输出结果为:
对于上半部分的,n = 9没有问题,当当以浮点型输出时就变了。
9的二进制为 00000000 00000000 00000000 00001001
当看做为浮点数时 0 00000000 000000000000000000001001
其值为 1.001*2(-146) 是一个特别小的数字,所以输出结果就为0.000000
对于下半部分,
9.0在计算机中的存储为 0 10000010 00100000000000000000000
如果为整数,则为 01000001 00010000 00000000 00000000
则为1091567616,是一个特别大的数字