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

时间:2013-06-12 10:23:17

标签: c++ c pow

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

好的,这是C代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

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编译器。

4 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:1)

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

  • 您使用的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实现显然没有这么设计。

答案 2 :(得分:0)

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

当然,在某些情况下,编译器甚至可能“知道”exp(log(i)*j)实际实现的内容,并用不变的结果替换计算。

答案 3 :(得分:0)

要在此处添加其他答案:在使用浮点值时,通常只需非常

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

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