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

        那么好了好了,宝子们,今天给大家介绍一下 “数据在内存中储存” 的来龙去脉---浮点数,来吧,开始整活!⛳️   

一、数据类型家族(浮点数) 

家族成员:float,double,long double 

二、浮点数在内存中的存储

1.抛砖引玉 :

通过下面的例题一,我们来引入浮点数在内存中的存储是怎么样的?

#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
int main()
{
	int n = 9;
	float* p = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("p的值为:%f\n", *p);
	*p = 9.0;
	printf("n的值为:%d\n", n);
	printf("p的值为:%f\n", *p);
	return 0;
}

当我们没有运行,咱们可以猜想一下这4个结果分别是什么呢?

我相信很多小伙伴和我当时想的一样,那么就是:

9

9.000000

9

9.000000

是这个样子的,但是我们看一下实际运行结果是什么样的?

        结果和我们想的不一样,错了一半,为什么呢?我们接下来就开始解释一下这个浮点数在内存中到底是怎样存储的?


三、浮点数存储的关键表达式:浮点数(v)=(-1)^S*M*2^E


 

1.  根据国际标准 IEEE (电气和电子工程协会) 754 ,任意一个二进制浮点数 V 可以表示成下面的形式:
        (-1)^S * M * 2^E
        (-1)^s表示符号位,当 s=0 V 为正数;当 s=1 V 为负数。
        M表示有效数字,大于等于 1 ,小于 2
        2^E表示指数位
2.  我们通过举例来进行对表达式的剖析:
十进制:5.5
二进制:101.1(小数点前的最后一个数字的权重是2^0,那么小数点后按顺序来说就是2^(-1)
,2^(-2)……以此类推。)
转化:1.011*2^2---->(-1)^0*1.011*2^2(S=0,M=1.011,E=2)
        接下来我们以float为数据类型来讲解一下浮点数在内存中的存储:
表达式中的字母顺序是S M E,但是浮点数在内存中存储的顺序是S E M
对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。

 对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

但是别看的这么简单,还有一个特别需要注意的点,就是E和M的储存都有特殊情况:

      3.   IEEE 754对有效数字 M 和指数 E ,还有一些特别规定。 前面说过, 1≤M<2 ,也就是说, M 可以写成 1.xxxxxx 的形式,其中xxxxxx 表示小数部分。
        IEEE 754规定,在计算机内部保存 M 时,默认这个数的第一位总是 1 ,因此可以被舍去,只保存后面的 xxxxxx 部分。
        比如保存1.01 的时候,只保存 01 ,等到读取的时候,再把第一位的 1 加上去。这样做的目的,是节省 1 位有效数字。
        以32 位浮点数为例,留给 M 只有 23 位,将第一位的 1 舍去以后,等于可以保存 24 位有效数字。(在这种情况下可以提高数据的精度)
     4.   至于指数E,情况就比较复杂:(E的存入)
        首先,E 为一个无符号整数( unsigned int
        这意味着,如果E 8 位,它的取值范围为 0~255 ;如果 E 11 位,它的 取值范围为0~2047 。但是,我们知道,科学计数法中的 E 是可以出现负数的,所以 IEEE 754 规定,存入内存时E的真实值必须再加上一个 中间数(无论E是正负,都需要加上中间数来修正
        对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023
        比如,2^10 E 是10 ,所以保存成 32 位浮点数时,必须保存成 10+127=137 ,即 10001001。
     5.  (E的取出):
指数 E 从内存中取出还可以再分成三种情况:
(1)E 不全为 0 或不全为 1
这时,浮点数就采用下面的规则表示,即指数 E 的计算值减去 127 (或 1023 ),得到真实值,再将有效数字 M 前(还原)
加上第一位的 1 。 比如: 0.5 1/2 )的二进制形式为 0.1 ,由于规定正数部分必须为 1 ,即将小数点右移 1 位,
则为 1.0*2^(-1) ,其阶码为 -1+127=126 ,表示为 01111110 ,而尾数 1.0 去掉整数部分为 0 ,补齐 0 23
00000000000000000000000 ,则其二进制表示形式为
0  01111110 00000000000000000000000
(2)E 全为 0
这时,浮点数的指数 E 等于 1-127 (或者 1-1023 )即为真实值,
有效数字 M 不再加上第一位的 1 ,而是还原为
0.xxxxxx 的小数。这样做是为了表示 ±0 ,以及接近于 0 的很小的数字。
(3)E 全为 1
这时,如果有效数字 M 全为 0 ,表示 ± 无穷大(正负取决于符号位 s );
好了,关于浮点数的表示规则,就说到这里。

四、首尾呼应(对前面例题的解释)


  对前半部分的分析:

    int n = 9;
    float* p = (float*)&n;
    printf("n的值为:%d\n", n);//9(在这一步我们以整数的形式放进n里面,再以%d整形来打印出来,完全没有问题,答案就是9)
    printf("p的值为:%f\n", *p);//0.000000

/*9------>0 0000000000000000000000000001001(二进制)

当我们以浮点数的视角来打印出来的时候,我们要分别求出来S M E。

0        00000000        00000000000000000001001

S        E                        M  

1.当E是全0的时候,E=1-127即为真实值。 

2.有效数字M不再加上第一位的1,而是还原为
0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
浮点数v=(-1)^0*0.00000000000000000001001*2^(-126)
因为以%f的形式打印出来浮点数,最终的结果只有小数点的后6位 : 0.000000*/
     对后半部分的分析:

    *p = 9.0;
    printf("n的值为:%d\n", n);//1091567616

/*因为上一部分的代码*p是浮点数,所以我们以浮点数的形式储存9.0。

9.0

1001.0=1.001*2^3

又因为9.0是正数,所以s=0

9.0==(-1)^0*1.001*2^3

内部的储存结果如下:

0        10000010        00100000000000000000000

S        E                        M        

当我们以整形的形式打印出来的时候,又因为它是个正数,所以说打印出来的时候正数的原码反码补码相同:

最终的结果直接就是他:01000001000100000000000000000000

然后再以二进制读出来:1091567616*/
    printf("p的值为:%f\n", *p);//这个地方等于9.000000没有疑问

 

 

好了,今天的分享就到这里了

如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!

请添加图片描述

 

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小能软糖_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值