如果你在 C++ 里写过这样的代码:
运行后可能会惊讶地发现,输出是 “不相等!实际值是: 0.3”。更奇怪的是,打印出来的明明是 0.3,为什么比较却不相等?
问题的根源:二进制表示的限制
这其实不是 C++ 的 bug,而是几乎所有使用IEEE 754浮点数标准的编程语言都会遇到的问题。根本原因在于:计算机用二进制表示小数,而有些十进制小数在二进制中是无限循环的。
0.1 在二进制中是多少?
十进制 0.1 转换成二进制是:0.00011001100110011…(无限循环)
就像 1/3 在十进制中是 0.33333…(无限循环)一样,0.1 在二进制中也是一个无限循环小数。但计算机的存储空间是有限的,所以必须进行舍入。
浮点数在内存中如何存储?
以常用的 double 类型(双精度浮点数)为例:
-
1 位符号位
-
11 位指数位
-
52 位尾数位
因为存储空间有限,0.1 和 0.2 在存入内存时已经被近似处理了。当它们相加时,误差会累积:
输出可能是:
看到了吗?0.1 + 0.2 的结果是 0.30000000000000004441,而直接写的 0.3 是 0.29999999999999998890。虽然它们都非常接近 0.3,但不完全相等。
正确的比较方式
既然不能直接用
==比较,那该怎么办?方法1:使用误差范围(epsilon)
方法2:使用相对误差
对于非常大的数或非常小的数,固定误差范围可能不合适,这时可以用相对误差:
方法3:C++ 的 std::numeric_limits
实际编程中的建议
-
避免直接比较:不要用
==或!=直接比较浮点数 -
合理选择误差范围:根据你的应用场景选择合适的 epsilon
-
注意累积误差:多次运算会累积误差,在循环中尤其要注意
-
考虑使用定点数:如果涉及金钱等需要精确计算的场景,考虑使用整数(以分为单位)或专门的高精度库
-
排序和容器的键:浮点数不适合作为容器的键值,因为微小的误差就会导致查找失败
不只是 0.1 + 0.2
这个现象很常见:
-
0.3 – 0.2 != 0.1
-
1.1 + 2.2 != 3.3
-
3.0 * 0.1 != 0.3
总结
浮点数精度问题不是 C++ 独有的,而是由计算机底层表示方式决定的。理解这一点能帮助你:
-
写出更健壮的数值计算代码
-
避免调试时的困惑
-
在合适的场景选择合适的数值类型
记住:浮点数是近似表示,不是精确表示。比较它们时,永远要考虑”在误差范围内是否相等”,而不是”是否完全相等”。
希望这篇文章能帮助你理解这个看似奇怪的现象。下次看到 0.1 + 0.2 != 0.3 时,你就知道这不是 bug,而是计算机科学的一个基本特性。