浮点型数据在内存中的存储

Posted Suk_god

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浮点型数据在内存中的存储相关的知识,希望对你有一定的参考价值。

浮点数的二进制表示

首先,计算机内存中存储的所有数据均以二进制的形式存在。那么,浮点数也不例外。要了解浮点数在内存中具体如何存储,首要问题是掌握浮点数二进制的表示方法。

基本表示方法

整数部分:除以2,商继续除以2,得到0为止,将余数逆序排列。

小数部分:乘以2,然后取出整数部分,将剩下的小数部分继续乘以2,然后再取整数部分,一直取到小数部分为零为止。如果永远不为零,则按要求保留足够位数的小数,最后一位做0舍1入。将取出的整数顺序排列

举例:22.8125 转二进制的计算过程:

整数部分:
22 / 2 11 余0
11/2 5 余 1
5 /2 2 余 1
2 /2 1 余 0
1 /2 0 余 1
得到22的二进制是10110

小数部分:

0.8125x2=1.625 取整1, 小数部分:0.625
0.625x2=1.25 取整1, 小数部分:0.25
0.25x2=0.5 取整0, 小数部分:0.5
0.5x2=1.0 取整1, 小数部分:0,停止

得到0.8125的二进制是0.1101

结果:十进制22.8125等于二进制00010110.1101

IEEE754国际标准表示

对于任意一个二进制浮点数V可以表示成下面的形式:

(-1)^S * M * 2^E
(-1)^S 表示符号位,S=0,V为正数,S=1,V为负数
M表示有效数字,取值范围 [1,2)
2^E 表示指数位

IEEE754规定:对于32位的浮点数,最高的一位是符号位S,接着的8位是指数E,剩下的23位为有效数字M,具体如下图所示

举例说明:
十进制浮点数6.0的二进制表示为:110.0,相当于1.1* 2^2,所以按照上面的公式来看,S=0,M=1.1,E=2


对于M IEEE754做出以下特别规定:
在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。
比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。
以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字

那么,如何保存01呢??我们规定,保存方向为自左向右依次保存,剩余比特位自动按0填充。

对于指数E来说,情况较为复杂:

首先,E为一个无符号整数(unsigned int) why?
原因是:便于识别,易于存取。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位浮点数时,必须保存成10+127=137即10001001

指数E取出来时会有三种情况:

①E不全为0或不全为1:
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1,得到浮点数的二进制序列表示,最后按照公式(-1)^S * M * 2^E带入求出十进制的浮点数。
②E全为0
此情况E的真实值就是0-127(或者0-1023),按照上面的方式进行还原后我们发现十进制表示为(-1)^S * M * 2^(-127),对于这个式子分析我们可以发现,当S=0时,它的值由正向无限接近于0值,当S=1时,由负向无限接近于0。与M的大小无关,因为M最终的表示形式仅仅是1.XXX,对整个结果影响微乎其微。


由上图我们可以得到一个结论:那就是浮点数不能直接使用“==”来判断两个数是否相等

int main()
{
	float f1 = 3.7;
	printf("%.50f\\n",f1);
	printf("%.50f\\n",3.7);
	if (f1  == 3.7)
		printf("f1==3.7\\n");
	system("pause");
	return 0;
}

这种写法是错误的。
浮点数和double型数在计算机中并不能精确存储,所以看似相等,但是在计算机中存储的二进制不一定相等,所以浮点数的比较是否相等,一般是让两个浮点数相减求绝对值,绝对值在某个范围内就认为相等,至于这个范围是多大是根据实际情况决定的。

③E全为1
此时E的真实值为127+127(1023+1023),那么(-1)^S * M * 2^E表示的值根据S的取值具体表现为浮点数的最大值或是最小值。也就是说,当E为全1时,表示该浮点数的最值问题。

小试牛刀

判断下面程序段的输出结果

int main()
{
 int n = 9;
 float *pFloat = (float *)&n;
 //代码1
 printf("n的值为:%d\\n",n);
 printf("*pFloat的值为:%f\\n",*pFloat);
 //代码2
 *pFloat = 9;
 printf("num的值为:%d\\n",n);
 printf("*pFloat的值为:%f\\n",*pFloat);
 return 0; }

对于这段代码我们分块理解:
上面这块表示:
第一条输出语句以%d的形式输出n,毋庸置疑,结果是9

第二条输出语句以%f的形式对指针变量pFloat所指向的目标进行输出。首先,指针指向n,n存储的内容是9的二进制表示,即0000 0000 0000 0000 0000 0000 0000 1001,现在指针也指向这里,但是输出时取数据是按照什么标准呢?第一步就是要看数据类型。而此时是按照%f的格式输出,那么系统就会将会9的二进制表示当做是浮点数来看待,也就是0 00000000 00000000000000000001001,所以对应E=0的情况,所以就是浮点数的0值,输出结果应该是:0.000000

对于代码2来说:
首先,通过指针的方式对变量n重新赋值,而在执行*pFloat = 9;这条语句时,由于左右两边类型不一致,所以会发生隐式类型转换,整型9转变成float类型后再进行赋值。按照浮点数的存储方式,我们可以得到9.0的二进制表示为1001.0–>1.001 * 2^3—>S=0;M = 1.001;E=3。通过计算,我们可以得到实际存储的二进制序列是0100 0001 0001 0000 0000 0000 0000 0000
所以,第一条输出语句以%d形式输出n值时结果时:二进制序列0100 0001 0001 0000 0000 0000 0000 0000最高位为0,为正数,结果就是此二进制序列对应的十进制数

而以%f输出*pFloat时,结果就是我们的9.0

以上就是对这道题的理解·~~

以上是关于浮点型数据在内存中的存储的主要内容,如果未能解决你的问题,请参考以下文章

深度剖析数据在内存中的存储之浮点型在内存中的存储

梦开始的地方 —— C语言数据在内存中的存储(整形+浮点型)

C语言学习 -- 整型与浮点型在内存中的存储

C语言从青铜到王者第五篇·数据在内存中的存储

C语言从青铜到王者第五篇·数据在内存中的存储

C语言从青铜到王者第五篇·数据在内存中的存储