<p>浮点数比较出错是因为二进制无法精确表示大多数十进制小数,如0.1在内存中为无限循环二进制小数而产生舍入误差;直接用==判断几乎总返回false,应改用误差范围比较,如abs(a - b) < ε。</p>

为什么 float 和 double 比较会出错
因为二进制无法精确表示大多数十进制小数,比如 0.1 在内存里是无限循环二进制小数,存进去就带误差。直接用 == 判断两个 double 是否相等,几乎总是错的。
- 常见错误现象:
0.1 + 0.2 == 0.3返回false - 正确做法:改用「误差范围比较」,比如
abs(a - b) - 注意
1e-9不是万能阈值——double有效位约 15–17 位,float只有 6–7 位,大数值时相对误差会放大 - 别用
std::numeric_limits<double>::epsilon()</double>直接当阈值,它表示的是1.0附近的最小可区分差值,不是通用容差
什么时候该用 long double 或整数缩放
long double 在不同平台差异极大:x86_64 Linux 通常是 80 位扩展精度(≈19 位十进制),Windows MSVC 却常退化成普通 double;整数缩放则完全规避浮点误差,但要自己管单位和溢出。
- 适用场景:金融计算、固定小数位计数(如价格用
int存「分」)、物理模拟中已知量级且需确定性 - 整数缩放示例:把
123.45存成12345(单位为0.01),所有加减乘除都对整数做,最后再除以100 - 风险点:
long double的sizeof和行为不跨平台一致,CI 构建环境可能和本地不同 - 别指望
long double解决所有问题——它只是延迟了精度瓶颈,不是消除它
std::setprecision 和 std::fixed 只影响输出,不改变值本身
很多人以为调了输出格式就能“修复”精度,其实只是掩盖问题。打印出来的 0.300000 还是那个不等于 0.3 的数。
- 典型误用:
cout 输出 <code>0.3,但后续参与计算仍用原值 - 真正需要控制显示时,优先用
std::round(x * 100) / 100.0显式舍入(注意:这会引入新误差) - 调试时建议用
printf("%.17g", x)查看真实存储值,%g能暴露隐藏的尾部数字
使用 std::abs 而不是裸写 fabs,并小心模板推导
C++ 标准库重载了 std::abs,能适配 float/double/long double,而 C 风格的 fabs 是函数族,容易因隐式转换导致精度丢失或编译失败。
立即学习“C++免费学习笔记(深入)”;
- 错误写法:
fabs(1.5f)可能调用double fabs(double),把float升级再降级 - 推荐写法:
std::abs(x),依赖 ADL 或标准重载,类型安全 - 模板函数里尤其要注意:如果参数是模板类型
T,直接写std::abs(t)比fabs(t)更可靠 - 别忽略
#include <cmath>—— 缺失时某些编译器可能静默链接到 C 版本,行为不一致
浮点数的“误差”不是 bug,是硬件和数学共同决定的特性。真正难的不是记住怎么写容差,而是判断当前场景下——这个误差是否会影响逻辑分支、是否会在多次迭代中累积、是否暴露给用户造成困惑。这些得靠具体数值范围和业务语义来定,没法套公式。

















