为什么直接比较两个浮点数是否“完全相等”是危险的?
【摘要】 我们从一个非常反直觉的点说起,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表示的是 。0.5表示的是 (也就是 )。
这很直观。
2. 二进制的视角(计算机的视角)
计算机用二进制,每一位只能表示 0 或 1。小数部分的位置,代表的是 2 的负幂次方:
- 第1位:
- 第2位:
- 第3位:
- 第4位:
- …以此类推
现在,我们试着用这些“二进制碎片”来拼凑出 0.1:
0.5太大了,比 0.1 大,所以第一位是 0。 (当前值: 0)0.25也太大了,第二位是 0。 (当前值: 0)0.125还是比 0.1 大,第三位是 0。 (当前值: 0)0.0625比 0.1 小!所以第四位是 1。 (当前值: 0.0625)- 我们现在还差
0.1 - 0.0625 = 0.0375。
- 我们现在还差
- 下一个是
0.03125,比 0.0375 小,所以第五位是 1。 (当前值: 0.0625 + 0.03125 = 0.09375)- 现在还差
0.1 - 0.09375 = 0.00625。
- 现在还差
- 下一个是
0.015625,比 0.00625 大,所以第六位是 0。 - 下一个是
0.0078125,还是比 0.00625 大,所以第七位是 0。 - 下一个是
0.00390625,比 0.00625 小,所以第八位是 1。 (当前值: 0.09375 + 0.00390625 = 0.09765625)- 现在还差
0.1 - 0.09765625 = 0.00234375。
- 现在还差
- 下一个是
0.001953125,比 0.00234375 小,所以第九位是 1。 (当前值: 0.09765625 + 0.001953125 = 0.099609375)- 现在还差
0.000390625。
- 现在还差
你会发现,这个过程会一直持续下去,永远无法用 2 的负幂次方精确地加起来等于 0.1。它会进入一个 循环模式。
实际上,0.1 的二进制表示是:
0.000110011001100110011001100110011… (其中的 0011 部分会无限重复)
3. 这就像在十进制里表示
这其实和一个我们熟悉的十进制问题很像:如何在十进制下精确表示 ?
答案是 ,它是一个 无限循环小数。无论你写多少个3,它都不是精确的 ,只能无限接近。
- 十进制下的
- 二进制下的 (也就是 0.1)
它们在自己的数制系统里,都是 无法被精确表示 的无限循环小数。
结论
所以,当你说 float16(0.1) 时,计算机只能把这个无限循环的二进制小数 截断 到 float16 所能容纳的有限位数(16位中的一部分用于小数)。这个被截断的、近似的结果就是 0.0999755859375。
这也就是为什么在编程中,直接比较两个浮点数是否“完全相等”是危险的,因为它们底层可能存储着你意想不到的微小误差。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)