C语言常见的浮点数类型有float和double。
常见的浮点数有小数形式 3.14、科学计数法形式 1E10(1.0 * 10^10)。
为了引出标题,同时更好地理解浮点型数据的存储与读取方式,这里举例一段代码。
例:浮点型与整型数据打印数据的对比
#include
int main()
{
int a = 10; // 00000000000000000000000000001010
float* pf = (float*) & a;
printf("整型存储,整型读取打印:a = %d。\n\n", a);
printf("整型存储,单精度浮点型读取打印:a = %f。\n\n", *pf);
*pf = 10.1F;
printf("单精度浮点型存储,单精度浮点型读取打印:a = %f。\n\n", *pf);
printf("单精度浮点型存储,整型读取打印:a = %d。\n\n", a);
return 0;
}
运行结果和预期的不一样。
a和*pf是同一个数、甚至是同一块内存空间,为什么读取打印结果差别这么大?
原因就是整型数据在内存中存储、读取的方式 ,与浮点型数据在内存中存储、读取的方式不一样。
整型数据的存储与读取方式(原码反码补码)
国际标准IEEE1754规定,一个浮点数二进制可以表示成:(-1)^S * M * 2^E
- (-1)^S 表示符号位,S为0是正数,S为-1是负数;
- M 表示有效数字,范围:M >= 1 && M < 2;
- 2^E 表示指数位。
例如十进制10.5,写成二进制就是1010.1;0.5写成二进制是0.1,是因为小数位权重是从2^-1开始。
按照浮点数表示形式,可以得出:S = 0,M = 1.0101,E = 3,说白了就是1.0101 * 2 ^ 3,二进制的科学计数法。
对于32位浮点数(float)是这样规定的:
- 最高位存储符号位 S;
- 后面8位存储指数位 E;
- 剩下23位存储有效数字 M。
对于64位浮点数(double)是这样规定的:
- 最高位存储符号位 S;
- 后面11位存储指数位 E;
- 剩下52位存储有效数字 M。
IEEE1754对存储有效数字M、指数E还有一些规定:
- 既然M大于等于1,小于2,总是为1.xxx的形式,那么存储有效数字时默认省略1,而只存储小数位。
如1.0101只保存0101,读取时会默认给前面加上1.。
这样能多出1位有效位,虽然内存只给23位存储有效位,但实际上可以存储24位有效位。
- E 为无符号整数,对于32位浮点数,E 只有8位,数值范围是0-255;
对于64位浮点数,E 有11位,数值范围是0-2047。
但科学计数法可以表示负数,所以IEEE1754规定 E 存入内存时需要加入一个中间数。
对于8位的E,中间数为127;对于11位的E,中间数为1023。
例如:2^-1,E为-1,保存E时必须先加上中间数:-1 + 127 = 126 再存储,即
01111110
。
至于 E 的读取分为三种情况:
E既有1也有0。将读取到的8位指数 - 127或1023得到真正的指数。
如上面例子:126 - 127 = -1 得到指数-1,加上有效数就是1.xxx * 2^-1。
E为全0。指数的真实值:1 - 127 = -126,读取有效数时不再补上省略的1,
那么有效数为0.xxx,0.xxx * 2^-126 那么这将是一个无线趋近于0的数。
E为全1。如果有效数为全0,读出的数值为正负无穷大,正负取决于符号位。
有了上面的知识,这时就可以解释最开始的例子了。
#include
int main()
{
int a = 10;
float* pf = (float*) & a;
printf("整型存储,整型读取打印:a = %d。\n\n", a);
printf("整型存储,单精度浮点型读取打印:a = %f。\n\n", *pf);
*pf = 10.1F;
printf("单精度浮点型存储,单精度浮点型读取打印:a = %f。\n\n", *pf);
printf("单精度浮点型存储,整型读取打印:a = %d。\n\n", a);
return 0;
}
第7行和第12行打印10和10.500000这个是毋庸置疑的,就是第8行和第13行打印出来和预期不一样。
第8行为什么是0.000000,用上面的知识能解决疑问。
a在内存中的补码为:00000000000000000000000000001010
读取打印是按照%f单精度浮点型,这时是将a当做浮点数来看待的。
第9行打印出来的数字很大,那是因为a被float类型指针重新解引用赋值10.5,是按照浮点数存储的,而这时却又按照%d整型读取打印。10.5的二进制数是1010.1,按照浮点数存储是:01000001101010000000000000000000
这串二进制数以整型读取出来,是一个很大的数,和打印结果一致。
电气和电子工程协会 ↩︎ ↩︎ ↩︎