C语言学习记录——이십칠 数据存储(4)

目录

浮点型在内存中的存储 

(-1)^S * M * 2^E

对于M:

对于E:

存进去

从内存中取出时指数E取出时的三种情况

E不全为0或不全为1

E为全0

E为全为1


浮点型在内存中的存储 

浮点数家族包括:float、double、long、long double类型。

浮点数表示范围:float.h中定义。可查询float.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;
}

要明白这个结果,需要先清楚浮点数在内存是如何存储的。

一个二进制浮点数的存储有一个公式,根据国际标准IEEE754(电气和电子工程协会)的规定,二进制浮点数v可表示为以下形式:

(-1)^S * M * 2^E

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

M表示有效数字,大于等于1,小于2。

E表示指数位。

带回到刚才的程序。一个浮点数9.0,二进制数就是1001.0,是正数,所以S为0;对于1001,要写成科学计数法,就是1.001,也就是M,而E,小数点移动了三位,所以E是3。所以写出来就是

(-1)^ 0 * 1.001 * 2 ^ 3。

浮点数在内存中存储时,32位的浮点数,最高位是S符号位,往后8位是E, 之后23位是M。64位的浮点数,最高位是S,E占11位,M占52位。

S不必多说

对于M:

小数点前面的1存入后,32位中还剩下22位可以存储,而M一定大于等于1,小于2,所以去掉前面的1,用23个bit来存储小数点后面的数字,使精度上升一位,去掉的1在读取时再加上。

对于E:

首先,E是一个无符号整数,32位时范围是0-255,64位时范围是0-2047。作为指数位,E有时候会是负数形式,比如输入0.5,它的二进制数是0.1.0可以理解,小数点后的1则表示2^-1,也就是0.5,所以0.5的二进制表示就是0.1。0.1存储时按照科学计数法,就会变为(-1)^ 0 * 1.0 * 2 ^ -1。所以为了避免这种情况,IEEE规定,存入内存值时E必须再加上中间值。对于8位的E,中间值是127,11位的E,中间值是1023。所以-1最终存储时就会变成126。

存进去

那么针对于9.0,这个浮点数怎样存进去的呢?这个过程很重要。前面已经说过,9.0的科学计数法写法就是(-1)^ 0 * 1.001 * 2 ^ 3。S为0,M为1.001,E为3 + 127 = 130。那么存储时整个二进制数就是 0  10000011  00100000000000000000000(M要补齐到23位),当去查看内存时,要转换成16进制数,2到16,以四个二进制数做分割, 0100 0001 1001 后面全是0。挨个转换数,41900..。所以当查看内存时,9.0的内存地址就是0x41900.....。这样就是整个存进去的过程。

从内存中取出时指数E取出时的三种情况

E不全为0或不全为1

指数E的存储值-127得到E的真实值,然后将有效数字M前加上第一位的1(之前去掉的1)。

9.0,拿出来时,E:130-127 = 3,M加上之前去掉的1,就变成(-1)^ 0 * 1.001 * 2 ^ 3。

E为全0

E如果为0,也就是8个0作为二进制位,E+127后等于0,所以E真实值为-127。放到科学计数法里,2^-127,是一个非常小的数字,无限接近于0,而加上前面的S,+-,所以整体数字就是一个无限接近于0的数字,数字的表达也很麻烦。所以统一规定,浮点数的指数E等于1-127(或1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxx的小数。这样做是为了表示+_0,以及接近于0的很小的数字。

E为全为1

意味着E真实值为128,也就是这将是一个正负无穷大的数字。关于此 IEEE的规定很多,不需要再细究。只记住是无限大的数字即可。

那么在回到最一开始的例子。

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,补码为0  00000000  00000000000000000001001,强制类型转换为float后,E为全0,那么它的表达就是 (-1) ^ 0 * 0.0000000000000000001001 * 2 ^ -126。这个数字其实就是一个正0,无限接近于0的数字,所以printf("*pFloat的值为: %f\n", *pFloat)打印出的结果就是0.000000。

*pFloat = 9.0后,9.0是一个浮点数。9.0的科学计数法表示就是(-1)^0 * 1.001 * 2^3,那么存到内存后就变为0 10000010 001000....。接下来打印的是%的,所以整个这个二进制数就是一个补码,所以很大,0 10000010 00100000000000000000000。下一个输出就9.0。

整个数据在内存中的存储写完了。

结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值