c#:float存储原理(待续。。。)

环境:

  • window 10
  • 处理器 11th Gen Intel® Core™ i7-11700
  • .net 6
  • vs2022

1. 基础准备:2进制、10进制、16进制转换

  • 常见2进制、10进制、16进制数据:
    在这里插入图片描述

  • 10进制整数转2进制算法:

- (8)10 => (1000)2

  • 10进制小数转2进制算法:
    在这里插入图片描述

  • 10进制数转2进制:
    在这里插入图片描述

  • 2进制整数转10进制:
    在这里插入图片描述

  • 2进制小数转10进制:
    在这里插入图片描述

2. 基础准备:科学计数法

  • 10进制: 123 => 1.23*102
  • 2进制:11 => 1.1*21

3. 基础准备:vs调试,如何查看内存?

下图显示,如何在vs调试时观察变量i的内存值:0x00000064 => 100
在这里插入图片描述
同样,查看数组在内存中的结构:
在这里插入图片描述
那么,查看float类型在内存中的结构(虽然还不明白float在内存中的存储规则。。。):
在这里插入图片描述
延伸一下,自己指定4个字节,让程序自动将它读取成float:
在这里插入图片描述

4. 基础准备:float类型存储规则简单示例

float是单精度浮点类型:使用32bit位(4字节)存储,存储时将32个bit位分为三部分,如下:
在这里插入图片描述

  • 第一部分:1个bit位,用S表示,存储0或1,分别表示正或负;
  • 第二部分:8个bit位,用E表示,存储的是指数的偏移值(原值+127);
  • 第三部分:23个bit位,用F表示,存储的是小数点后的数字;

先来个示例,看下102.25f在内存中怎么存储的?

  • 第一步: 102.25f转为2进制 1100110.01
  • 第二步:转成科学计数法为 1.1001_1001 * 2110,也可以表示为:1.1001_1001 * 26;(注意:小数点前面大多数为1,所以规定只存小数点后面的)
  • 第三步:分别填充
    • S位:正数,填充0;
    • E位:指数为6,加上偏移127后等于133,转为2进制填充为:1000_0101
    • F位:使用1001_1001填充,右侧补零,最终为:1001100_10000000_00000000
  • 第四步:最终102.25f的内存结构应为:
    在这里插入图片描述

使用vs调试工具验证:
在这里插入图片描述

5. 基础准备:从float内存结构反推具体值

核心是将上面的规则反走一遍。
还是以102.25f,为例,已知它的内存结构如下:

在这里插入图片描述

  • 第一步:分别读取S、E、F部分:
    • S部分为0,表示正数;
    • E指数部分为:133,减去偏移量127后,最终为:6
    • F尾数部分读取后是1.10011001(2进制),因为指数为6,所以要移6位,最后为:1100110.01(2进制);
  • 第二步:转成十进制(1100110.01=>102.25

6. 认识IEE754

其实,通过上面的示例,应该大致懂了float类型数据的存储规则,但是还不够细致。
下面就往细了说。
还是这张图:
在这里插入图片描述

问题:

  • 将10进制小数换算成2进制小数能换算尽吗?将2进制小数换算成10进制小数能换算尽吗?
  • 打印出来的float值真的是内存中存储的值吗?
  • 为什么微软文档说float类型的精度是:6-9位,而网上大多说是:7位?
  • 为什么float f=1.0058_5938f 打印出来是:“1.0058_5938f”,而float f3 = 1.0058_5939f打印出来也是:“1.0058_5938f”?

待续。。。。

10进制比二进制要精细,10进制的小数0.1的意思是1/10,那么用2进制怎么精确表示呢?
答案是:无法精确表示出来!

这就好比:有一块肉,我只需要平分10块取其1。而你有1把刀,每次只能将肉平分,这样无论你切多少次,切出来肉的数量只可能是:2,4,8,16,32,64。。。无论是哪一份我们都不可能挑出1/10的肉。

所以,float类型无法精确表示0.1f,但是float可以精确表示0.5f,无法表示0.11f,但可以精确表示0.25f。。。

如果我们把F位的23个bit位缩小到5个bit,它们能精确表示的离散10进制数是:
在这里插入图片描述
那么上面的数据有多少个呢? Math.Pow(2,5) = 32 个。

因为只有32个,所以我们可以通过4舍五入的方法将这32个离散数缩小到十进制数:
1位小数:可以表示10个;
2位小数:虽然不能表示所有,但可以表示0.25和0.75两个;
3位小数:同上,可以表示4个;
4位小数:同上,可以表示8个;
5位小数:上面已占用了24个(10+2+4+8),而我有16个,超过32个了,所以5位小数的都不要表示了;

注意,到了2位小数的时候就已经不能覆盖它的全部了,所以再往后其实意义不大,所以再往下多1位小数即可,最终表示的如下:
在这里插入图片描述
所以,5个bit位表示的小数范围是:1-3,其中1位小数能全部表示,而2、3位小数都只能是1部分。
又因为float类型指数部分取值可能是0或1,当它是1时,所能表示的精度要再加一,如:1.125的精度是4,
最终,5个bit位的精度是1-4(25=32)。

按照这个推算就不难理解为什么微软说float类型的精度是6~9了:

223=8388_608,所以表示6位小数肯定没问题,而7位、8位小数也可以表示1部分,又因为指数部分再加一,所以最终是:6-9位精度。

从2进制存储反推10进制的时候,对于5个bit位,它的精度是1-4,当反算出来的小数精度大于4时,比如:1.9375,因为它不在可表示范围内,所以要进行4舍5入,这时候分别对0.0005,0.007,0.03进行四舍五入,又因为四设五入后均不能满10进1达到缩短小数位数的效果,所以只对最后一位进行四舍五入即可,最终显示的结果为:1.938。

看float的数字:0x00C0803F,它的存储反推为:1.0058_5937_5,因为精度超过9(精度为10),所以要进行四设五入,而对
0.0000_003、0.0000_0007、0.0000_0000_5进行四舍五入均不能满足10进1达到缩小小数位数的效果,所以只能是将最后的5四设五入,所以最终得到:0.0058_5938f

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

jackletter

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

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

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

打赏作者

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

抵扣说明:

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

余额充值