为什么直接比较两个浮点数是否“完全相等”是危险的?

举报
黄生 发表于 2025/11/13 15:52:43 2025/11/13
【摘要】 我们从一个非常反直觉的点说起,0.1 在我们常用的十进制里,是一个有限小数,但在计算机使用的二进制里,它却是一个无限循环小数。我们来一步步拆解一下: 1. 十进制的视角(我们熟悉的)在我们的十进制世界里:0.1 表示的是 1÷101 \div 101÷10。0.5 表示的是 5÷105 \div 105÷10(也就是 1÷21 \div 21÷2)。这很直观。 2. 二进制的视角(计算机的视...

我们从一个非常反直觉的点说起,0.1 在我们常用的十进制里,是一个有限小数,但在计算机使用的二进制里,它却是一个无限循环小数。我们来一步步拆解一下:

1. 十进制的视角(我们熟悉的)

在我们的十进制世界里:

  • 0.1 表示的是 1÷101 \div 10
  • 0.5 表示的是 5÷105 \div 10(也就是 1÷21 \div 2)。

这很直观。

2. 二进制的视角(计算机的视角)

计算机用二进制,每一位只能表示 0 或 1。小数部分的位置,代表的是 2 的负幂次方:

  • 第1位: 121=0.5\frac{1}{2^1} = 0.5
  • 第2位: 122=0.25\frac{1}{2^2} = 0.25
  • 第3位: 123=0.125\frac{1}{2^3} = 0.125
  • 第4位: 124=0.0625\frac{1}{2^4} = 0.0625
  • …以此类推

现在,我们试着用这些“二进制碎片”来拼凑出 0.1

  1. 0.5 太大了,比 0.1 大,所以第一位是 0。 (当前值: 0)
  2. 0.25 也太大了,第二位是 0。 (当前值: 0)
  3. 0.125 还是比 0.1 大,第三位是 0。 (当前值: 0)
  4. 0.0625 比 0.1 小!所以第四位是 1。 (当前值: 0.0625)
    • 我们现在还差 0.1 - 0.0625 = 0.0375
  5. 下一个是 0.03125,比 0.0375 小,所以第五位是 1。 (当前值: 0.0625 + 0.03125 = 0.09375)
    • 现在还差 0.1 - 0.09375 = 0.00625
  6. 下一个是 0.015625,比 0.00625 大,所以第六位是 0
  7. 下一个是 0.0078125,还是比 0.00625 大,所以第七位是 0
  8. 下一个是 0.00390625,比 0.00625 小,所以第八位是 1。 (当前值: 0.09375 + 0.00390625 = 0.09765625)
    • 现在还差 0.1 - 0.09765625 = 0.00234375
  9. 下一个是 0.001953125,比 0.00234375 小,所以第九位是 1。 (当前值: 0.09765625 + 0.001953125 = 0.099609375)
    • 现在还差 0.000390625

你会发现,这个过程会一直持续下去,永远无法用 2 的负幂次方精确地加起来等于 0.1。它会进入一个 循环模式

实际上,0.1 的二进制表示是:
0.000110011001100110011001100110011… (其中的 0011 部分会无限重复)

3. 这就像在十进制里表示 13\frac{1}{3}

这其实和一个我们熟悉的十进制问题很像:如何在十进制下精确表示 13\frac{1}{3}

答案是 0.3333333...0.3333333...,它是一个 无限循环小数。无论你写多少个3,它都不是精确的 13\frac{1}{3},只能无限接近。

  • 十进制下的 13\frac{1}{3}
  • 二进制下的 110\frac{1}{10}(也就是 0.1)

它们在自己的数制系统里,都是 无法被精确表示 的无限循环小数。

结论

所以,当你说 float16(0.1) 时,计算机只能把这个无限循环的二进制小数 截断 到 float16 所能容纳的有限位数(16位中的一部分用于小数)。这个被截断的、近似的结果就是 0.0999755859375

这也就是为什么在编程中,直接比较两个浮点数是否“完全相等”是危险的,因为它们底层可能存储着你意想不到的微小误差。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。