【C语言】浮点型数据在内存中的存储、读取方式

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;
}

运行结果和预期的不一样。
【C语言】浮点型数据在内存中的存储、读取方式_第1张图片


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行打印出来和预期不一样。

【C语言】浮点型数据在内存中的存储、读取方式_第2张图片



第8行为什么是0.000000,用上面的知识能解决疑问。

a在内存中的补码为:00000000000000000000000000001010

读取打印是按照%f单精度浮点型,这时是将a当做浮点数来看待的。

【C语言】浮点型数据在内存中的存储、读取方式_第3张图片

第9行打印出来的数字很大,那是因为a被float类型指针重新解引用赋值10.5,是按照浮点数存储的,而这时却又按照%d整型读取打印。10.5的二进制数是1010.1,按照浮点数存储是:01000001101010000000000000000000
【C语言】浮点型数据在内存中的存储、读取方式_第4张图片

这串二进制数以整型读取出来,是一个很大的数,和打印结果一致。

【C语言】浮点型数据在内存中的存储、读取方式_第5张图片


  1. 电气和电子工程协会 ↩︎ ↩︎ ↩︎

你可能感兴趣的:(C语言,c语言)