调用pow()时舍入结果的差异

好的,我知道有很多关于powfunction的问题,并将结果转换为int,但我找不到这个问题的答案。

好的,这是C代码:

#include  #include  #include  int main() { int i = 5; int j = 2; double d1 = pow(i,j); double d2 = pow(5,2); int i1 = (int)d1; int i2 = (int)d2; int i3 = (int)pow(i,j); int i4 = (int)pow(5,2); printf("%d %d %d %d",i1,i2,i3,i4); return 0; } 

这是输出:“25 25 24 25”。 请注意,仅在第三种情况下,pow的参数不是文字,我们有错误的结果,可能是由舍入错误引起的。 如果没有明确的强制转换,同样的事 有人可以解释这四种情况会发生什么吗?

我在Windows 7中使用CodeBlocks,以及随附的MinGW gcc编译器。

pow操作的结果是25.0000加上或减去一些舍入误差。 如果舍入误差为正或零,则转换为整数将导致25。 如果舍入误差为负,则结果为24。 两个答案都是正确的。

内部最有可能发生的情况是,在一种情况下,直接使用更高精度的80位FPU值,而在另一种情况下,结果是从FPU写入内存(作为64位双倍)和然后回读(将其转换为略微不同的80位值)。 这可以在最终结果中产生微观差异,这就是将25.0000000001更改为24.999999997所需的全部内容

另一种可能性是你的编译器识别传递给pow的常量并进行计算本身,用结果代替对pow的调用。 您的编译器可能使用内部任意精度数学库,也可能只使用不同的数学库。

这是由两个问题的组合引起的:

  • 您使用的pow的实施质量不高。 在许多情况下,浮点运算必然是近似的,但良好的实现需要注意确保诸如pow(5, 2)类的简单情况返回精确结果。 您正在使用的pow返回的结果小于25的数量大于0但小于或等于2 -49 。 例如,它可能会返回25-2 -50
  • 您正在使用的C实现有时使用64位浮点格式,有时使用80位浮点格式。 只要数字保持为80位格式,它就会保留pow返回的完整值。 如果将此值转换为整数,则会生成24,因为该值小于25并且转换为整数截断; 它不圆。 当数字转换为64位格式时,它将被舍入。 在浮点格式之间进行舍入舍入,因此将结果四舍五入到最接近的可表示值25.之后,转换为整数将产生25。

在某种意义上,编译器可以在“方便”时切换格式。 例如,有80位格式的寄存器数量有限。 当它们已满时,编译器可能会将某些值转换为64位格式并将它们存储在内存中。 编译器还可以在编译时重新排列表达式或执行部分表达式而不是运行时,这些可能会影响执行的算法和使用的格式。

当C实现混合浮点格式时很麻烦,因为用户通常无法预测或控制格式之间的转换何时发生。 这导致结果不易再现并且干扰导出或控制软件的数值特性。 可以将C实现设计为始终使用单一格式并避免其中一些问题,但您的C实现显然不是那么设计的。

我很确定这可以通过“中间舍入”和pow不是简单地循环j次乘以i ,而是使用exp(log(i)*j)作为浮点计算来计算。 中间舍入可以很好地将24.999999999996转换为25.000000000 – 即使任意存储和重新加载该值也可能导致这种行为的差异,因此根据代码的生成方式,它可能会对确切的结果产生影响。

当然,在某些情况下,编译器甚至可以“知道”实际上实现了什么,并用恒定的结果替换计算。

要在此处添加其他答案:使用浮点值时通常要非常小心。

我强烈建议您阅读本文(尽管阅读时间很长): http : //hal.archives-ouvertes.fr/docs/00/28/14/29/PDF/floating-point-article.pdf

跳到第3节的实际例子,但不要忽视前面的章节!