C++11中double计算58/10出现精度偏差如何得到精确5.8结果
问题本质
你遇到的尾差是double类型的固有特性,不是计算bug。double是IEEE 754标准定义的二进制双精度浮点数,5.8转换为二进制是无限循环小数,无法用有限长度的二进制位精确存储,你看到的5.7999999999999998就是内存中实际存储的、和5.8最接近的双精度浮点值。
额外提醒:你贴的示例代码double result = 58/10;中,58和10都是整型字面量,C++会优先执行整数除法得到结果5,再隐式转换为double存储为5.0,和你描述的5.7999...结果不符,你实际运行的代码中两个操作数至少有一个是浮点类型,比如58.0/10。
没有任何手段可以让标准IEEE 754双精度浮点数精确存储5.8这个值,所有能让你最终看到精确5.8输出的逻辑,本质都触发了舍入,和你要求无舍入的场景冲突。要实现无舍入的精确计算和比对,可以用下面几种方案:
可选实现方案
- 换用十进制精确数值类型
如果编译器支持C20标准,可以直接用标准库提供的std::decimal::decimal64/decimal128十进制浮点类型,这类类型按十进制规则存储数值,可以精确表示所有有限十进制小数,不会出现二进制浮点的尾差问题。
如果编译器不支持C20十进制类型,可以自行实现定点数运算:根据业务需要的小数精度,把所有数值放大对应倍数转为整数存储和运算,比如只需要1位小数精度就把所有值乘10存为long long类型,58/10直接做整数运算得到对应5.8的整数值58,输出时手动插入小数点即可,全程不会出现浮点精度误差,完全不需要舍入。 - 全等比对改为容差比对
如果业务要求必须使用double做计算,不要直接对两个浮点值做==全等判断,改为判断两个值的绝对差值是否小于预设的极小精度阈值,示例代码:
这种方式可以自动忽略二进制浮点存储带来的末尾精度误差,不会把和5.8仅差1e-15量级的存储值判定为不匹配。#include <cmath> const double precision_eps = 1e-9; bool value_match = fabs(calc_result - 5.8) < precision_eps; - 固定精度截断后再处理
如果业务只需要固定N位小数的有效精度,可以在计算完成后先按所需精度做截断,抹除末尾的浮点尾差后再做后续比对和输出,避免尾差传导到其他结果位。
内容的提问来源于stack exchange,提问作者user3917631
相关产品推荐
相关产品推荐

