<p>应使用 std::abs(a - b) < epsilon 判断浮点数近似相等,因 IEEE 754 浮点数是二进制近似表示,0.1+0.2≠0.3(实际为0.30000000000000004),直接 == 比较几乎总错误。</p>

直接用 == 判断两个 float 或 double 是否近似相等,几乎总是错的——这不是代码写得不够好,而是 IEEE 754 浮点表示本身就不支持“数学意义下的精确相等”。
为什么 std::abs(a - b) 不能无脑套用
这个表达式本身没错,但 epsilon 不是“精度开关”,它是个量纲敏感的容忍窗口。固定写 1e-9 在以下场景会出问题:
- 比较
1e20和1e20 + 1:差值是1,远大于1e-9,但 double 根本无法区分这两个数(它们在内存中可能完全一样) - 比较
1e-15和0.0:差值是1e-15,小于1e-9,看似“相等”,但此时1e-9比你关心的量级大了 6 个数量级,等于放弃精度 - 计算路径含 10 次乘加后得到的结果,其固有误差可能已达
1e-13,而你却用1e-15去比,大概率误判
std::numeric_limits<t>::epsilon()</t> 是什么、不是什么
它返回的是 1.0 附近能表示的最小增量,比如 double 的 epsilon 是约 2.22e-16。但它不等于通用比较阈值:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 它只对接近
1.0的数有意义;对1e10,实际可分辨最小差值是epsilon * 1e10 - 它不反映计算误差——一次
sin(x)调用的误差可能比它大几个数量级 - 别直接拿它当
epsilon传给比较函数,除非你明确在比较1.0附近的值
工业级写法:绝对+相对双判据 + 特殊值兜底
主流测试框架(Google Test、Catch2)和 NumPy 的 isclose 都采用这种结构,C++23 的 std::is_close 也照此设计:
立即学习“C++免费学习笔记(深入)”;
bool approx_equal(double a, double b,
double abs_tol = 1e-12,
double rel_tol = 1e-9) {
if (std::isnan(a) || std::isnan(b)) {
return std::isnan(a) && std::isnan(b);
}
if (std::isinf(a) || std::isinf(b)) {
return a == b;
}
double diff = std::abs(a - b);
if (diff <= abs_tol) return true;
double scale = std::max({std::abs(a), std::abs(b), 1.0});
return diff <= rel_tol * scale;
}-
abs_tol覆盖零值或极小值区域(1e-12对 double 较安全) -
rel_tol控制非零区域的相对精度(1e-9≈ 9 位有效数字) -
std::max(..., 1.0)避免除零,也防止 scale 过小放大误差 - 必须显式处理
NaN和inf:std::abs(NaN)还是NaN,后续所有比较都失效
容易被忽略的三个现实约束
再健壮的函数也救不了没想清楚的业务语义:
- 你的输入来自文件读取?那要考虑序列化/反序列化引入的额外舍入(比如 printf
"%.17g"写出再读回,可能已失真) - 是否在容器里当 key 用?
std::unordered_map<double int></double>默认用==,会导致相同逻辑值被存成多份——必须自定义哈希与比较仿函数 - 误差容忍是否随上下文变化?物理仿真中
1e-6可能太大,金融计算中连double都不该用,该切整数
















