如何比较浮点数相等_【强基固本】算法工程师应该了解的浮点数知识

本文深入浅出地介绍了浮点数的表示与存储原理,包括IEEE754标准下的符号位、有效数字及指数位的概念,并探讨了特殊情况下浮点数的表现形式。此外,还讨论了浮点运算中可能遇到的问题及其解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

“强基固本,行稳致远”,科学研究离不开理论基础,人工智能学科更是需要数学、物理、神经科学等基础学科提供有力支撑,为了紧扣时代脉搏,我们推出“强基固本”专栏,讲解AI领域的基础知识,为你的科研学习提供助力,夯实理论基础,提升原始创新能力,敬请关注。

作者:知乎-Dounm

地址:https://www.zhihu.com/people/niu-chong

作为一个算法工程师,工作中经常会用到浮点数相关的知识,比如要对比两个浮点数的误差,再比如混合精度在 float 和 half 之间做变换。因此,有必要了解一些浮点数相关的知识。

01

浮点数的表示

根据国际标准IEEE 754,任意二进制浮点数都可表示为a7343ae5-7f3a-eb11-8da9-e4434bdf6706.svg

  • aa343ae5-7f3a-eb11-8da9-e4434bdf6706.svg :符号位。 ad343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 代表正数, b1343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 代表负数。

  • b4343ae5-7f3a-eb11-8da9-e4434bdf6706.svg :有效数字,大于1,小于2

  • b6343ae5-7f3a-eb11-8da9-e4434bdf6706.svg :指数位

举例:

  • ba343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 的二进制是 be343ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,则 c2343ae5-7f3a-eb11-8da9-e4434bdf6706.svg

  • c6343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 的二进制是 c9343ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,则 cc343ae5-7f3a-eb11-8da9-e4434bdf6706.svg

  • d7343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 的二进制是 da343ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,则 e0343ae5-7f3a-eb11-8da9-e4434bdf6706.svg

02

浮点数的存储

首先我们要注意区分浮点数的「表示值」(下标 e2343ae5-7f3a-eb11-8da9-e4434bdf6706.svg )和「存储值」(下标 e5343ae5-7f3a-eb11-8da9-e4434bdf6706.svg )。

上一节的例子中, e8343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 的值都是表示值,但真正把这些浮点数存储在内存中时,具体存储的值并不等同与表示的值。

这一些都是因为 IEEE 754 对 eb343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 和 ed343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 的存储还有些特殊规定。

针对有效数字 M

对于有效数字 eb343ae5-7f3a-eb11-8da9-e4434bdf6706.svg , IEEE 规定其表示值必须满足 f1343ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,所以 f4343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 总是1.xxxxx的形式。

但 IEEE 规定存储 eb343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 时可以不存第一位的1,所以 f9343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 只保存xxxxx,即 fc343ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,这样就可以节省一位有效数字。

所以,前例中的 fe343ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,但实际只用存储 02353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 。

针对指数 E

对于指数E ,因为 E 是unsigned int,所以如果 E 有 8bit(Float),那么取值为0~255;如果E 有11bit(Double),取值为0~2047。

但科学计数法的 E 可以为负数,所以 IEEE 规定 06353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 。

对于8bit E (float),Exponent Bias 是 09353ae5-7f3a-eb11-8da9-e4434bdf6706.svg ;对于11bit E (double),Exponent Bias 是 0c353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 。

因此,前例中 0e353ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,那如果用 Float 数据类型来保存该数字,真正要保存在内存里的其实是 10353ae5-7f3a-eb11-8da9-e4434bdf6706.svg

除此之外,还有两种特殊情况:

  • 12353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 的 bits 全为0

以 float 数据类型为例,若按照默认规则,则 14353ae5-7f3a-eb11-8da9-e4434bdf6706.svg ;但实际上,对于此种情况,特殊规定 16353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 。

因此, 19353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 和 1c353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 对应的 1e353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 一样。但区别在于, 19353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 对应的有效数字的表示值 22353ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,即 0.xxxxx(与默认规定不同);而 1c353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 对应的有效数字的表示值 25353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 1.xxxxx(与默认规定相同)。

为什么要对 19353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 采用特殊规定呢?
因为采用默认规定的话,我们能表示的最小数字 29353ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,该num必然大于 2c353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 。
而如果采用上述特殊规定, 2e353ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,则可以表示更小的数字,即 Subnormal Numbers。

  • 12353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 的 bits 全为1
    如果此时 f9343ae5-7f3a-eb11-8da9-e4434bdf6706.svg 的 bits 全是0,则表示正负无穷大inf;否则就是NaN

03

总结

如果以存储值来表示任意浮点数的话(除去前面提到的两种特殊情况),其默认公式如下:38353ae5-7f3a-eb11-8da9-e4434bdf6706.svg

常见浮点数据类型

930c4f7d7c14f33296c78c767d87aa5d.png

对于任意浮点数来说,其最高位存的是sign;最低位存储Fraction bit。

关于表格最右侧的十进制有效数字,因为 3f353ae5-7f3a-eb11-8da9-e4434bdf6706.svg ,所以二进制精度如果是24位的话,那么十进制有效数字约7位。因此,对于 float16这种浮点数来说,其十进制有效数字就特别小,如果 debug 时看到两个 half 的区别特别大,也不用特别惊慌,这是正常现象 _(:3」∠)_

浮点数的一些其它相关

  • 如果两个浮点数相加会怎样?

更小的数字会被shifted right来保证exponent一致,方便加法。因此,该更小的数字会损失部分的significand精度(即round-off error,Floating-point arithmetic - Wikipedia)

  • 浮点数能否精确表示 43353ae5-7f3a-eb11-8da9-e4434bdf6706.svg ?

因为浮点数的存储也是基于二进制的,因此对于 43353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 这种无法用二进制精确表示的数字,实际上在计算机里存储的都是临近值(同理还有 4a353ae5-7f3a-eb11-8da9-e4434bdf6706.svg 等 )。

float a = 0.1;    cout << a << endl; // 输出0.1,因为cout的默认输出精度不高,所以会roundup为0.1cout.precision(10);   cout << a << endl; // 输出0.1000000015
  • 如何判断两个浮点数相等?

因为浮点数会有精度损失,因此我们通常并不会用 == 来判断两个浮点数相等,而是判断两者 diff 是否足够小,小到一定程度就认为两个浮点数相等。

此处可以参考 numpy.isclose(...) 的实现,通过公式 absolute(a - b) <= (atol + rtol * absolute(b)) 来判断两个浮点数是否相等。

Refer

[1] IEEE 754-1985 - Wikipedia

https://en.wikipedia.org/wiki/IEEE_754-1985

[2] 浮点数的二进制表示

http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html

本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。

0833ffb7b8401851960e627586c4decd.gif

直播预告

0833ffb7b8401851960e627586c4decd.gif

2f9a41cdf4c44884965a18f4c9d6a374.png

强基固本”历史文章

  • 神经网络量化简介

  • 样本量极少如何机器学习?Few-Shot Learning概述

  • 我们真的需要深度图神经网络吗?

  • 深度强化学习(Deep Reinforcement Learning)入门

  • 伪标签(Pseudo-Labelling)——锋利的匕首

  • 基础算法:使用numpy实现逻辑回归随机梯度下降(附代码)

  • 脉冲神经网络(SNN)

e434d46b5d2ce622bb682fffadb55cc8.png3aa5b7a82cf1e820c971a576d82f9bb9.gif

分享、点赞、在看,给个三连击呗!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值