C++中浮点数比较的坑:为什么0.1+0.2≠0.3?

C++
如果你在 C++ 里写过这样的代码:
#include <iostream>

int main() {
    double a = 0.1;
    double b = 0.2;
    double c = a + b;
    
    if (c == 0.3) {
        std::cout << "相等!" << std::endl;
    } else {
        std::cout << "不相等!实际值是: " << c << std::endl;
    }
    
    return 0;
}
运行后可能会惊讶地发现,输出是 “不相等!实际值是: 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 在存入内存时已经被近似处理了。当它们相加时,误差会累积:
#include <iomanip>
#include <iostream>

int main() {
    double a = 0.1;
    double b = 0.2;
    double c = a + b;
    
    std::cout << std::setprecision(20);  // 高精度输出
    std::cout << "0.1 实际存储为: " << a << std::endl;
    std::cout << "0.2 实际存储为: " << b << std::endl;
    std::cout << "0.1 + 0.2 = " << c << std::endl;
    std::cout << "0.3 实际存储为: " << 0.3 << std::endl;
    
    return 0;
}
输出可能是:
0.1 实际存储为: 0.10000000000000000555
0.2 实际存储为: 0.20000000000000001110
0.1 + 0.2 = 0.30000000000000004441
0.3 实际存储为: 0.29999999999999998890
看到了吗?0.1 + 0.2 的结果是 0.30000000000000004441,而直接写的 0.3 是 0.29999999999999998890。虽然它们都非常接近 0.3,但不完全相等

正确的比较方式

既然不能直接用 ==比较,那该怎么办?

方法1:使用误差范围(epsilon)

#include <cmath>
#include <iostream>

bool isEqual(double a, double b, double epsilon = 1e-10) {
    return std::abs(a - b) < epsilon;
}

int main() {
    double a = 0.1;
    double b = 0.2;
    double c = a + b;
    
    if (isEqual(c, 0.3)) {
        std::cout << "在误差范围内相等!" << std::endl;
    }
    
    return 0;
}

方法2:使用相对误差

对于非常大的数或非常小的数,固定误差范围可能不合适,这时可以用相对误差:
bool isEqualRelative(double a, double b, double epsilon = 1e-10) {
    if (a == b) return true;  // 包括两者都是0的情况
    
    double diff = std::abs(a - b);
    double maxVal = std::max(std::abs(a), std::abs(b));
    
    return diff <= maxVal * epsilon;
}

方法3:C++ 的 std::numeric_limits

#include <limits>
#include <cmath>

bool isEqualPrecise(double a, double b) {
    return std::abs(a - b) <= std::numeric_limits<double>::epsilon() 
           * std::max(std::abs(a), std::abs(b));
}

实际编程中的建议

  1. 避免直接比较:不要用 ==!=直接比较浮点数
  2. 合理选择误差范围:根据你的应用场景选择合适的 epsilon
  3. 注意累积误差:多次运算会累积误差,在循环中尤其要注意
  4. 考虑使用定点数:如果涉及金钱等需要精确计算的场景,考虑使用整数(以分为单位)或专门的高精度库
  5. 排序和容器的键:浮点数不适合作为容器的键值,因为微小的误差就会导致查找失败

不只是 0.1 + 0.2

这个现象很常见:
  • 0.3 – 0.2 != 0.1
  • 1.1 + 2.2 != 3.3
  • 3.0 * 0.1 != 0.3

总结

浮点数精度问题不是 C++ 独有的,而是由计算机底层表示方式决定的。理解这一点能帮助你:
  1. 写出更健壮的数值计算代码
  2. 避免调试时的困惑
  3. 在合适的场景选择合适的数值类型
记住:浮点数是近似表示,不是精确表示。比较它们时,永远要考虑”在误差范围内是否相等”,而不是”是否完全相等”。
希望这篇文章能帮助你理解这个看似奇怪的现象。下次看到 0.1 + 0.2 != 0.3 时,你就知道这不是 bug,而是计算机科学的一个基本特性。

购买须知/免责声明
1.本文部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责。
2.若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
3.如果本站有侵犯、不妥之处的资源,请在网站右边客服联系我们。将会第一时间解决!
4.本站所有内容均由互联网收集整理、网友上传,仅供大家参考、学习,不存在任何商业目的与商业用途。
5.本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
6.不保证任何源码框架的完整性。
7.侵权联系邮箱:aliyun6168@gail.com / aliyun666888@gail.com
8.若您最终确认购买,则视为您100%认同并接受以上所述全部内容。

会员源码网 C++ C++中浮点数比较的坑:为什么0.1+0.2≠0.3? https://svipm.com/21685.html

相关文章

猜你喜欢
发表评论
暂无评论