浮点数精度误差源于IEEE 754无法精确表示多数十进制小数,导致0.1+0.2≠0.3等现象;应使用绝对与相对容差组合比较,金融等场景须改用整数或字符串模拟十进制运算。

为什么 float 和 double 算出来总是差一点点?
因为 IEEE 754 浮点数在二进制下无法精确表示大多数十进制小数,比如 0.1 在内存里实际存的是一个无限循环二进制近似值。这不是 C++ 的 bug,是所有遵循该标准的语言共有的底层限制。
常见错误现象:0.1 + 0.2 == 0.3 返回 false;循环累加 0.1 十次后结果不是 1.0 而是 0.9999999999999999。
- 别用
==直接比较浮点数,改用「绝对误差」或「相对误差」判断 - 需要精确十进制运算(如金融计算)时,
float/double不适合,应换定点数或字符串/整数模拟 -
double比float多约 9 位有效数字,但依然不能解决根本问题——只是误差变小了
怎么安全地比较两个 double 是否“相等”?
核心是引入容差(epsilon),但选哪个 epsilon 很关键:用固定值(如 1e-9)在大数比较时会失效,用相对误差又得防零除。
推荐做法是组合使用绝对与相对容差:
立即学习“C++免费学习笔记(深入)”;
bool almost_equal(double a, double b, double abs_eps = 1e-9, double rel_eps = 1e-6) {
double diff = std::abs(a - b);
if (diff <= abs_eps) return true;
double norm = std::max(std::abs(a), std::abs(b));
return diff <= rel_eps * norm;
}-
abs_eps应对接近零的值(避免除零和相对误差失真) -
rel_eps控制数量级较大时的精度比例,一般取1e-6(对应float)或1e-12(对应double) - 别直接用
std::numeric_limits<double>::epsilon()做比较阈值——它表示的是 1.0 附近的最小可表示差值,不是通用容差
什么时候该放弃浮点、改用整数或字符串?
当业务逻辑要求“无损十进制”时,比如金额、税率、配置文件中的百分比、UI 动画时间戳(需帧间严格一致),浮点运算是危险的。
可行方案:
- 金额统一转为整数分:把
19.99存成1999,所有运算用long long,输出时再除以 100 - 解析用户输入的十进制字面量时,优先用
std::stod后立刻转整数缩放,而不是直接参与运算 - 配置项如
"timeout_ms": 300.5这种,JSON 解析器返回double是陷阱——应约定只接受整数毫秒,或单独字段"timeout_us" - 不要用
std::to_string回写浮点数做“精度修复”,它受 locale 和默认精度影响,不可靠
std::setprecision 和 std::fixed 只影响输出,不改变数值本身
很多人以为调了输出格式就能“修正”精度,其实只是掩盖问题。打印 0.1 + 0.2 显示 0.3,不代表它真是 0.3,后续计算仍按真实二进制值进行。
示例:
double x = 0.1 + 0.2; std::cout << std::setprecision(1) << std::fixed << x << "\n"; // 输出 "0.3" std::cout << (x == 0.3) << "\n"; // 输出 "0"
-
std::setprecision(n)对std::fixed是小数位数,对默认浮点格式是总有效位数 - 输出控制不能替代算法设计——该用整数的地方,光靠格式化没用
- 调试时建议用
std::hexfloat查看真实内存值,比十进制更暴露问题
最常被忽略的一点:误差会随运算次数指数级放大,尤其在迭代求解、矩阵变换、物理仿真中,单步误差小,百步之后可能完全偏离。这时候得考虑算法层面的数值稳定性,而不是只盯着类型选 float 还是 double。











