为什么C++中round()函数会导致我的表达式计算结果错误?
问题原因
这个差异是double类型的精度限制导致的,和设备无关,是C++隐式类型转换规则与浮点数存储特性共同造成的。
前置知识
- C++标准中
round函数的参数、返回值均为double类型,只要将整数传入round,或是和round的返回值做算术运算,都会触发隐式类型转换,将整数转为double。 - 64位双精度浮点数
double仅保留53位有效尾数,只能精确表示绝对值不超过2^53(约9007199254740992,即9e15)的整数。超过该范围的整数转成double时会被舍入到最近的可表示值,且在这个数值量级下对double做±1操作不会生效(相邻两个可表示double值的间隔已经大于1)。
计算过程拆解
变量
a的计算全程为long long类型的整数运算,没有精度损失,结果完全正确:10000000000000000 + 24822089714520516 - 1 = 34822089714520515,和输出结果一致。变量
b的计算触发了多次隐式类型转换,出现精度损失:- 首先
xpow(10LL, 16)返回的long long类型1e16传入round,会先转成double,1e16刚好可以被double精确表示,因此round返回的double值仍为1e16。 - 接下来
double类型的1e16和long long类型的cnt相加,cnt会被隐式转为double。你使用的cnt值为24822089714520516,远大于2^53,转double时会被舍入,最终得到的double值比原整数大1。 - 最后执行
-1操作时,相加后的结果已经是约3.4e16的double值,这个量级下double的最小精度间隔大于1,减1操作不会生效,最终得到的结果就比a多了1。
- 首先
修复建议
你自定义的xpow本身返回的就是精确整数,完全不需要调用round处理,直接使用原始返回值即可。如果确实有需要对整数做舍入的场景,要么先确认数值范围不会超过double的精确整数范围,要么改用整数实现的舍入逻辑,避免浮点数精度问题。
内容的提问来源于stack exchange,提问作者Trần Gia Bảo
相关产品推荐
相关产品推荐

