【浮点数】在内存中如何存储???简单理解

2024-07-20 笔记 - 3

浮点型在内存中的存储

32位的存储

V【浮点数编码 / 浮点数表示】 中有 S(符号位)、E(指数位)、M(尾数位或有效数字)

 

float32位存储比特位数double64位存储比特位数
S1bitS1bit
E8bitE11bit
EE
EE
EE
EE
EE
EE
EE
ME
ME
ME
M23bitM52bit
MM
MM
MM
MM
MM
MM
MM
MM
MM
MM
MM
MM
MM
MM
MM
MM
MM
MM
M - 到这里共23bitM
…52个bit位

 

64位的存储

常见的浮点数

普通写法 3.1415926

科学技术 1E10

① 【整型】的存储和获取的方式 与 【浮点型】的存储和获取的方式是 “不一样” 的
  • 如果以浮点数的形式存进去就要以浮点数的形式拿出来;

  • 如果以整型的形式存进去就要与整型的形式拿出来。

  • 如果用整型的形式存进去,用浮点的形式拿出来,拿出来会出问题;

  • 如果以浮点的形式存进去,用整数的形式拿出来,拿出来会出问题。

下面举例:

n 和 *pFloat 在内存中虽然是同一个数,但是一个是【整型】的取法,一个是【浮点】的取法,解读结果一定差别很大。

 #include <stdio.h>
 int main()
 {
     int n = 9;
     float* pFloat = (float*)&n;
     printf("n的值为:%d\n", n);//9 以整型的形式(9)存进去,还以整型的形式肯定可以拿出来
     printf("*pFlaot的值为:%f\n", *pFloat);//出错,打印出来是不对的 --> 以整数的形式(9)存进去,肯定不能以浮点的形式拿出来
     *pFloat = 9.0;
     printf("n的值为:%d", n)//出错,打印出来是不对的 --> 以浮点的形式(9.0)存进去,以整型的形式拿不出来
     printf("*pFloat的值为:%f\n", *pFloat);//9.0 --> 以浮点的形式(9.0)存进去,肯定可以以浮点的形式再拿出来
 }
浮点存储氛围三部分

比如int和flaot,以整型的形式进行存储的时候, int的32个bit位除了开头的符号位,其余31位全都是用来表示数值的。 而在float中,分成了三块,

32位

  • S【符号位】占1bit,表示符号

  • E【指数位】占8bit,可以理解为表示科学技术法中E后面的数字,E在内存中存储的值叫做【存储指数值】,E表示的数值叫做【实际指数】

  • M【尾数 / 有效数字】占23bit,可以理解为表示科学计数法中E前面的数字

64位

  • S【符号位】占1bit,表示符号

  • E【指数位】占11bit,可以理解为表示科学技术法中E后面的数字,E在内存中存储的值叫做【存储指数值】,E表示的数值叫做【实际指数】

  • M【尾数 / 有效数字】占52bit,可以理解为表示科学计数法中E前面的数字

后面会进行解释

② 浮点数 的存储规则

要先了解 国际标准 IEEE(电气和电子工程协会)754,浮点数用 V 来表示的。
  • (-1)^S * M * 2^E

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

  • M 表示有效数字,范围为 1 <= M < 2

  • 2^E 表示指数位

如何更好的理解呢???

举例1:

 十进制 - 5.0
 二进制 - 101.0  --> 1.01 * 2^2
 V格式  - S = 0   M = 1.01   E = 2 == (-1)^S * M * 2^2

举例2:

【小数部分】转换成二进制的方式和【整数部分】装换成二进制的方式不一样

整数部分:计算的时候次幂是从【0】开始的

 101 –>  (1 * 2^2) + (0 * 2^1) + (1 * 2^0) --> 5

小数部分:计算的时候次幂是从【-1】开始的

 0.1 --> 1 * 2^-1 --> 0.5
 0.101 --> (1 * 2^-3) + (0 * 2^-2) + (1 * 2^-1) --> 0.125 + 0.25 + 0.5 = 0.875
 十进制 - 5.5
 二进制 - 101.1  --> 1.011 * 2^2
 V格式  - S = 0   M = 1.011   E = 2 == (-1)^S * M * 2^2

注: 即使是0.××M不会出现0.××

 十进制 - 0.5
 二进制 - 0.1
 V表示  - S = 0, M = 1.0, E = -1 == (-1)^0 * 1.0 * 2
 指数在内存中其实并不是存储的-1,后面会讲

③ IEEE 754 的< 特别规定 > 针对于 “有效数字M”和“指数E”
  • 有效数字 M

首先我们知道M的取值范围是,1 <= M < 2(M是不可能大于2的,因为二进制表示的小数全是 1 或 0,不存在大于2的情况),M一般是1.××××××××××××××××××××××(22个×,1个1 共23位)

IEEE 754 规定,在计算机内部保存M的时候,默认这个数的第一位总是1,因此可以被舍去,只保存后面的××××××××××××××××××××××××(23个个×,就不存1了,这样子就会节省出来,相当于间接的存储了开头的1,可以理解为就间接的存储了24位bit数)这一部分,这样开头一位就可以不占用空间了,从而多出来一位。

比如1.1101(二进制表示),只存储1101,等到读取的时候,再把第一位的1加上去,这样可以节省一位“有效数字” ,使其多了一位可以表示有效数字的bit位。

疑惑:M中的小数点算一位吗??? 不算

④ 为什么浮点数总是不那么精准呢???

有一些可以的出准确的浮点数,有一些却不可以,和二进制存储有关系,后面的位数可能就是差那么一点点就能算出来,所以好多时候算出来的都是近似值,总会丢一些二进制位。

  • 指数 E

注: E 为无符号整数unsigned int。

32位的E 的 偏移量 - 127 64位的E 的 偏移量 - 1023

疑问:E是无符号的那怎么表示负数呢???

存: 将【真实指数 / 实际指数】计算成【存储指数值 / 编码指数值】存入到内存中。

以32位的float为例

E因为是无符号的,所以肯定不可能再存入负数了,只能进行其他的“转换”进行存储。

这里就出现了一个东西,叫做“中间数”,可以让负数进行偏移,这样就做到了将负数偏移成可以存入无符号内存中的正数

比如想存入指数值E是 -1,我们计算肯定是按照-1【真实指数 / 实际指数】计算的,可是内存中可不是这么存储的,在存入内存的时候,用了一种奇特的方式,即存入 -1 + 127(-1 + 偏移量),叫做【存储指数值 / 编码指数值】所以是以数值126存储到指数部分的。

读: 当这个浮点数被读取并应用于计算时,计算过程会从存储的指数值126 - 127(偏移量) = -1 然后将可以将这个恢复后的指数值-1正确的参与到后续的浮点数计算中。

读取的三种方式 - 其实也会影响 M 中的开头位的值

① E 有 1 也有 0 时

【存储指数值】 - 【偏移量 / 中间值】 = 【实际指数】 通过这种计算就可以将存储到内存中的【存储指数值】正确的读取出来,读取出来的值就是【实际指数】

下面用32位的float为例进行解释,也为了进一步加深自己的理解

 5.5
 101.1
 S = 0   M = 1.011  E = 2
 E - 00000010【实际指数】 - 2
 E - 10000001【存储指数值】 - 129 == 2 + 127
 下面写的是该浮点数在内存中正确的存储
 0 10000001 01100000000000000000000 - 总共32位
 S = 0, E = 10000001, M = 01100000000000000000000
 总之读取的时候,E其实转换成00000010了,就是直接减去127

总而言之就是计算的时候要将存储的指数读取位实际指数进行计算。

总结:

  • 将E的从内存中存储的【存储指数值】 - 【偏移量】 = 【实际指数】读取出来

  • 将有效数字M的开头位置在加上 1,还原成 1.×××××××

  • 进行计算,计算公式:**(-1)^S * M * 2^E**,得出最终的【浮点数】

② E 全为 0 时(特殊情况)

其实是 -127 + 127

E全为0的时候,E的【存储指数值】可是-127,但是这种的统一按照存储指数为【1】来的,1 - 1271 - 1023得出【实际指数】【-126】【-1022】

  • E的【实际指数】126 / 1022

  • M有效数字不是加上1了,而是加上0,即还原成 0.×××××××,这用作是为了表示±0.××这种正负无穷的无限接近于0的数字。

③ E 全为 1 时(特殊情况)

有效数字全为1,表示±无穷大

因为当E全都为1 的时候,E的【存储指数值】是255,那么【实际指数】就是128,2 ^ 128 已经是一个非常非常大的数了,所以直接表示无穷大就可以了。

总之简单来说就是:

  1. 存储时,我们把实际的指数(可能是负数)加上一个固定的数(偏移量),这样就变成了一个正数。

  2. 当我们读取这个数时,我们再把这个正数【存储指数值】减去那个固定的数,这样就得到了原来的指数值,可能是负数。

通过这个方法,即使我们用无符号的数(只能表示正数)来存储指数,我们也能正确地处理正数和负数的指数。

本人小白,在学习中,这就当做笔记了,可能很多不对的地方,请网友们提出更改意见哈哈。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值