数据在内存的存储(2)【浮点数在内存的存储】

一.浮点数以什么形式存储在内存中

根据根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V都可以存储为这样的形式:

V=(-1)^S*M*2^E。

(1)(-1)^S表示符号位,当S为0时,V为正数;S为1时是负数。

(2)M表示有效数字,M是大于等于1,小于2的

(3)2^E表示指数位。

举个例子:比如5.0,写成二进制是101.0,相当于1.01*2^2。则S=0,M=1.01,E=2.

                  比如-5.0,相当于-1.01*2^2。则S=1,M=1.01,E=2.

                  再比如5.5,二进制是101.1,所以为1.011*2^2.则S=0,M=1.011,E=2.

IEEE 754规定:

对于32位的浮点数(float),最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M.

对于64位的浮点数(double),最高的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M.

这就是关于浮点数在内存中最基础的存储。

二.浮点数存的过程

前面我们说,M我们可以写成1.xxxxx的形式,其中xxxxx是小数部分。当我们保存M的时候,默认这个数的第一位总是1。因此我们可以把这个1舍去,只保存后面的xxxxxx。比如我们存储1.011的时候,只保存011,等到读取的时候,再把第一位的1加上去。这样我们就可以节省出1位有效数字,就像是M本来只能储存23位,但是省去了第一位后,相当于我们最多可以存储24位。

关于E的存储,就有些复杂了,首先它必须是一个无符号的整数。假如E为8位,它的取值范围就是0~255,如果为11,就是0~2047。但是我们知道,这个是指数,科学计数法里是可以出现负数的,所以IEEE 754规定,存入内存的E必须加上一个中间数,对于8位的E,中间数就是127。11位的就是1023.比如,2^10,我们保存成32位时,我们需要保存成137,也就是10001001。

大家注意,float类型是有最大值和最小值的。所以我们的E肯定也是有最大值和最小值的。不过我们不用担心我们加上127或者1023会超出这个范围。

来给大家举个例子

至于为什么是反过来的,可以看我上一篇博客,关于大小端存储的内容。

注意这里我举的例子是5.5在内存中可以精确保存的,但是像是3.14,1.1等等这些数字,是无法再内存中精确保存的。因为我们数据是以二进制存储的,小数点后面是2^-1,2^-2等等,这样的数据对于32位的float是无法精确保存的。

三.浮点数取的过程

1.E不全为1或不全为0

比如0.5的存储

0 01111110 0000000000000000000000

这个最好理解,当我们把存入内存的数据取出来的时候,先把指数E减去127或者1023,得到真实值,再将有效数字M前加上第一位的1(因为我们在存的时候不是把这个省去了吗)。整体的步骤实际上就是把我们存进去的时候完全反了过来。

2.E全为0

0 00000000 00100000000000000000000

这是一种特殊情况,这个时候的E等于1-127或者1-1023这个就是真实值,有效数字M不再加上第一位的1,而是还原成0.xxxxxx的小数。这样做是为了表示±0,以及接近0的很小的数字。

3.E全为1

0 11111111 00100000000000000000000

这个也是一个特殊情况,这里我们有八个1,表示的十进制数是255,大家可以想想,因为我们是加完127之后得到的这个数字,那么真实值就是255-127=128。那么也就是说,这个数字是2^128这是一个天文数字。所以它就表示的是正负无穷大的数字。

四.练习

来看一下这四个输出的是什么结果。

#include<stdio.h>
int main()
{
	int a = 9;
	float* pfloat = (float*)&a;
	printf("a=%d\n", a);
	printf("*ploat=%f\n", *pfloat);

	*pfloat = 9.0;
	printf("a=%d\n", a);
	printf("*ploat=%f", *pfloat);
	return 0;
}

这个就要用到我刚才写的那些内容了。

第一个printf就不用多说,输出的肯定是9。

第二个值是几呢?9的二进制是1001,补成32位之后是00000000000000000000000000001001,这里我们把a的地址取出来,因为&a的类型是int*所以这里肯定要强制转换成float*。此时的pfloat指向的就是一个float*类型的a。站在pfloat的角度,它会认为自己指向的是一个float的类型。所以这里的32位就会被理解成是用浮点数存储的方式。也就是

0 00000000 00000000000000000001001

这种情况就是我们上面说的E为全0的特殊情况。E的值就是-126,有效数字M也不再加上第一位的1.所以这里就是(-1)*0.00000000000000000001001*2^-126这个值就直接约等于0了。

第二个打印出来结果:0.000000

第三个的结果我们是把浮点数类型的9.0用%d打印出来,其实只要理解了第二个这一个也不算难了。回想一下浮点数在内存的存储。9.0的二进制是1001.0也就是(-1)^01.001*2^3.S=0,E=3,M=1.001。所以0 10000010 00100000000000000000000这个就是9.0在内存的存储。但是这里打印的是用%d打印,认为内存里的是有符号说,所以这里的最高位0代表的就是正。而正数的原码,反码和补码都是一样的。所以我们就按照这个原码来打印出来。

第三个打印出来的结果:1,091,567,616

这那么最后一个打印出来的这个就是以浮点数的形式打印的,所以就是9.000000

好了,到这里就把数据在内存中的存储就全部讲完了。感谢大家的观看,如有错误,请大家多多指出。

  • 33
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值