<p>不能直接用 == 比较浮点数,因为其二进制近似表示导致 0.1 + 0.2 ≠ 0.3(实际为 0.30000000000000004),== 判断会返回 false;这是 IEEE 754 标准的必然现象,应改用 std::abs(a - b) < epsilon 判断。</p>

为什么不能直接用 == 比较浮点数
因为浮点数在计算机中是二进制近似表示,0.1 + 0.2 不等于 0.3(实际是 0.30000000000000004),直接用 == 判断会返回 false,即使数学上相等。这不是 bug,而是 IEEE 754 标准下的必然现象。
常见错误现象:std::abs(a - b) 看似合理,但对极大或极小数值会失效——比如比较 <code>1e20 和 1e20 + 1,差值远超 1e-9,但它们在浮点精度下本应视为“相同”;而比较 1e-15 和 0 时,绝对误差阈值又可能过于宽松。
推荐用相对误差 + 绝对误差组合判断(ULP 不强制要求时)
兼顾大小数场景的稳健做法:先检查是否“都接近零”,再用相对误差。标准库不提供现成函数,需手动写:
bool float_equal(double a, double b, double abs_tol = 1e-9, double rel_tol = 1e-9) {
double diff = std::abs(a - b);
if (diff <= abs_tol) return true;
return diff <= rel_tol * std::max(std::abs(a), std::abs(b));
}-
abs_tol防止小数值下分母过小导致相对误差失真(如a和b都是1e-10级) -
rel_tol控制相对偏差比例,对大数更合理(比如1e6允许误差约1e-3) - C++23 引入了
std::is_close(在<cmath>中),行为与上述一致,参数名也相同,可直接用
需要更高精度或跨平台一致性?考虑 ULP 比较
ULP(Unit in the Last Place)是从二进制表示角度衡量“相邻浮点数间隔”的单位。它能真正反映浮点数的离散精度结构,比固定误差阈值更本质。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
例如 std::nextafter(1.0, 2.0) 和 1.0 相差 1 ULP;对任意两个数,可转为整数位模式后算差值绝对值,再与容忍 ULP 数比较:
int64_t ulp_diff(double a, double b) {
auto ia = std::bit_cast<int64_t>(a);
auto ib = std::bit_cast<int64_t>(b);
return std::abs(ia - ib);
}
// 使用:ulp_diff(a, b) <= 4 表示最多允许 4 ULP 差异- 注意正负零、NaN、无穷大会影响
std::bit_cast行为,需提前过滤 - ULP 方法对所有数量级一视同仁,但实现稍重,且 C++20 才有
std::bit_cast,旧版本需用memcpy或联合体绕过 strict aliasing - 若只是业务逻辑判断(如 UI 显示、配置校验),通常相对+绝对误差已足够;ULP 更适合数值算法验证、测试断言等场景
实际使用中容易忽略的关键点
很多人只改阈值,却没注意类型和上下文:
- 用
float就别套double的1e-9阈值——float有效位约 7 位十进制,1e-6更稳妥 - 涉及计算链(如
a = x * y + z),误差会累积,阈值要适当放大,不能照搬理论精度 - 比较结果不可传递:
float_equal(a,b)和float_equal(b,c)为真,不代表float_equal(a,c)一定为真 - 如果是在容器查找(如
std::map<double, T>),不要重载operator<基于近似相等——这会破坏严格弱序,导致未定义行为
最常被跳过的步骤是:确认你的“极小误差”到底由什么决定——是传感器精度?算法收敛容差?还是用户感知阈值?选错依据,再严谨的实现也解决不了问题。

















