为什么不能使用"=="运算符判断两个浮点数是否相等

简介: 对于大多数员程序来说,浮点数是相当冷门的知识,但这是一个很有意思的话题。本文从一个简单的相等比较操作分析了计算过程对浮点数精度的影响并给出了在编程实践中完全禁止使用"=="运算符去判断两个浮点数是否相等的结论。

在大多数编程语言中,使用"=="运算符判断两个浮点数是否相等的结果都是难以确定的,并且几乎总是无意义的,在编程实践中应该完全禁止使用"=="运算符去判断两个浮点数是否相等。

一个问题及其分析

如若不信,可以先试着运行下面这段js代码,看看它的输出结果:

console.log(0.1 + 1 - 1 == 0.1);

在Chrome Console中,上面这句代码的输出是false,意味着(0.1+1-1)这个数不等于0.1! 看上去加减法互为逆运算的这一基本运算法则都被颠覆了,这究竟是什么原因呢?

计算机科学作为应用数学的一个分支,和纯数学相比有一个显著的特点,那就是无法摆脱物理规律限制。浮点数的计算精度及其带来的一系列问题正是这一特点在存储字长受限上的体现。在详细解读这句js代码之前,我们不妨先来看看另外一个问题:

先将有理数1/7 [无限循环小数 0.1(428571)] 以小数形式写在一张最多只能容纳9个数字的小纸条上,得到0.14285714;再将这个数加上10并把结果写到第二张同样长的纸条上,最后一个数字4由于纸条太短写不下,只好舍去,得到10.1428571;然后将第二张纸条上的数字减去10的结果写到第三张纸条上,得到0.1428571。此时,如果认为第一张纸条上的数字是1/7, 第三张纸条上的数字是(1/7 + 10 - 10), 就会得出(1/7 + 10 - 10)不等于1/7的荒谬结论!然而,不论是第一张还是第三张纸条上的数字,都不是1/7的精确数值,而是1/7的近似值,并且近似的精度还不一样,所以它们不相等。

尽管浮点数在计算机器中的表示比上面所举的纸条示例要更复杂,但Javascript里的(0.1+1-1)这个数之所以和0.1不相等,与上面例子中(1/7 + 10 - 10)之所以不等于1/7并无本质上的不同:两者都使用了有限空间来存储无限小数,必然只能存储一个近似值,而不同的计算过程影响了近似值的精度。显然,如果使用"=="运算符对两个有着不同精度的近似值逐位进行比较,有可能会得到false的判断结果。

下面具体分析在Javascript中浮点数0.1经过先加1再减1的操作过程后,最终如何得到了一个不同于0.1的数。

JavaScript的Number类型都是64位浮点数[1],按照IEEE 754标准,0.1的存储模式为:

  • 最高位(第63位)为符号位,0,表示是正数
  • 第52-62这11位为指数部分,01111111011,转成十进制数为1019,表示指数值为1019-1023=-4
  • 第0-51这52位为小数部分,1001100110011001100110011001100110011001100110011010 [十进制数0.1的二进制表示是无限循环小数 0.(1001), 第50-51位的10是由精确值的第50-52位011舍入(rounding)第52位上的1进位后得到], 这样,小数值为二进制数1. 1001100110011001100110011001100110011001100110011010 [根据标准,小数部分的最高位总为1,于是被省去,这里需要加回],将其转成十进制是一个非常接近1.6的数。最终,1.6*2^(-4)=0.1

1的存储模式为:

  • 符号位0
  • 指数值为0
  • 小数值为1. 最终,1*2^(0)=1

0.1+1的处理过程为: 将0.1的小数部分右移4位(可见部分的最高3位以及隐藏的最高位1),得到小数部分为0001100110011001100110011001100110011001100110011010 (第50-51位的10由精确值的第50-52位011舍入第52位上的1进位后得到),同时将指数值加上4变为0,让它与1对齐;将指数值设为0,再将小数部分相加,即为0.1+1的最后结果1.1 (这里由于1的小数部分仅有最高隐藏位为1,相加后正好被隐去,于是结果的小数部分无变化)。

1.1-1的处理过程为: 两者的指数值都为0,无需移位对齐;两者的小数部分的隐藏最高位相减后被抵消,1的小数部分全为0,所以其余位无变化;此时的小数部分的最高位不是1,需要进行规约化(normalize)使最高位为1,具体操作为左移4位,同时将指数值减去4变为-4,并在最低4位补0。最后结果的小数部分为1001100110011001100110011001100110011001100110100000。

可见,(1.1-1)的最低6位和0.1是不一样的,这就是(0.1 + 1 - 1 == 0.1)被判断为false的原因。

正确的比较方式

function fpeq(a, b) {
    let eps = 1e-08;
    return Math.abs(a-b) < eps;
}

console.log(fqeq(0.1 + 1 - 1, 0.1));

延伸阅读

  • 浮点数的二进制表示
  • "4.2.1 Single-Precision Calculations" in The Art of Computer Programming, Vol 2: Seminumerical Algorithms, Third Editioin, by D. E. Knuth, 1998
  • "2.4 Floating Point" in Computer Systems: A Programmer's Perspective, by R. E. Bryant and D. R. O'Hallaron, 2003

[1] http://www.w3schools.com/js/js_numbers.asp

目录
相关文章
|
自然语言处理 安全 C++
【C++ 格式化输出 】C++20 现代C++格式化:拥抱std--format简化你的代码
【C++ 格式化输出 】C++20 现代C++格式化:拥抱std--format简化你的代码
8976 4
|
10月前
|
Linux
【Linux】System V信号量详解以及semget()、semctl()和semop()函数讲解
System V信号量的概念及其在Linux中的使用,包括 `semget()`、`semctl()`和 `semop()`函数的具体使用方法。通过实际代码示例,演示了如何创建、初始化和使用信号量进行进程间同步。掌握这些知识,可以有效解决多进程编程中的同步问题,提高程序的可靠性和稳定性。
464 19
|
IDE API 开发工具
RFSoC应用笔记 - RF数据转换器 -02- IP配置指南(二)
RFSoC应用笔记 - RF数据转换器 -02- IP配置指南
1373 0
RFSoC应用笔记 - RF数据转换器 -02- IP配置指南(二)
|
负载均衡 监控 安全
Istio:微服务治理的超级英雄,一键解锁你的服务网格超能力,让管理复杂变简单!
【8月更文挑战第31天】随着云原生技术的发展,微服务架构成为主流,但其复杂性与管理难题也随之增加。Istio作为开源服务网格平台,通过独特的数据平面和控制平面设计,实现了微服务通信的透明管理,简化了治理复杂度。本文将对比Istio与传统微服务管理方法,详细介绍Istio的架构及其工作原理,包括Envoy代理、服务发现、负载均衡、流量管理、安全认证以及监控等功能。Istio不仅简化了微服务治理,还提供了强大的流量控制和安全机制,使开发者能更高效地管理应用。
395 2
|
机器学习/深度学习 存储 算法
聚类算法:Kmeans和Kmeans++算法精讲
聚类算法:Kmeans和Kmeans++算法精讲
4860 0
|
数据安全/隐私保护
ev4a/ev6/ev8/evs/evpalyer2加密视频去水印翻录录屏教程
遇到.ev4a/.ev6/.ev8/.evs格式视频无法正常播放,及录屏时出现黑屏问题?本教程教你轻松解决!首先确保你已获得播放授权简要流程:1) 使用指定播放器打开加密视频;2) 运行破解工具解除录屏限制;3) 使用推荐工具完成录屏。快速转换为MP4格式,让你的视频分享无忧!请注意合法合规使用。
|
JSON API 数据安全/隐私保护
python小知识-数据验证和解析神器pydantic
Pydantic是一个Python库,用于数据验证和设置管理,基于类型提示提供数据模型验证。它可以用于用户输入验证、JSON序列化和解析,以及API交互中的数据校验。安装Pydantic可使用`pip install -U pydantic`或`conda install pydantic -c conda-forge`。通过定义BaseModel子类并使用Field进行约束,可以创建数据模型并进行验证。例如,定义User模型验证用户名、邮箱和年龄。Pydantic还支持自定义验证器,允许在字段赋值时执行特定逻辑,如密码强度检查和哈希处理。5月更文挑战第19天
756 1
|
自然语言处理 算法 数据挖掘
自然语言处理 Paddle NLP - 情感分析技术及应用SKEP-实践
自然语言处理 Paddle NLP - 情感分析技术及应用SKEP-实践
186 0
|
JavaScript
什么是NaN?以及如何检查值是否为NaN?
什么是NaN?以及如何检查值是否为NaN?
305 0
|
分布式计算 Hadoop 关系型数据库
Hadoop中的Sqoop是什么?请解释其作用和用途。
Hadoop中的Sqoop是什么?请解释其作用和用途。
243 0