C++ 调用pow()时四舍五入结果的差异

C++ 调用pow()时四舍五入结果的差异,c++,c,pow,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)d

好的,我知道有很多关于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;
}
#包括
#包括
#包括
int main()
{
int i=5;
int j=2;
双d1=功率(i,j);
双d2=功率(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);
返回0;
}
这就是输出:“25 24 25”。请注意,只有在第三种情况下,pow的参数不是文本,我们才会得到错误的结果,可能是由舍入错误引起的。没有明确的施法,同样的事情也会发生。有人能解释一下这四种情况下会发生什么吗


我正在使用Windows 7中的代码块,以及附带的MinGW gcc编译器。

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

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


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

我确信这可以用“中间舍入”来解释,
pow
不是简单地循环
j
乘以
I
,而是使用
exp(log(I)*j)进行计算
作为浮点计算。中间舍入可能会将24.9999999996转换为25.000000000-即使是任意存储和重新加载值也可能会导致这种行为的差异,因此根据代码的生成方式,可能会对精确结果产生差异


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

在这里补充其他答案:在处理浮点值时通常要非常小心

我强烈建议你阅读这篇文章(尽管这是一篇很长的文章):


跳到第3节获取实际示例,但不要忽略前面的章节

这是由两个问题共同造成的:

  • 您正在使用的
    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实现显然不是这样设计的。

您希望确切解释什么?24和25都是正确答案。实现只做最有效的事情。我的输出是
25
。这很奇怪。@user2448027尝试禁用所有优化。在Ubuntu系统中,我也得到了“25”,这只在Windows中发生。只有在第三种情况下,这才是最有趣的。@Marko如果禁用Ubuntu中的所有优化,会发生什么?舍入永远不会将大于25的值更改为小于25的值。即使你不知道浮点运算的规则或机制,术语“round”也是一条重要的线索:24.99999997并不比25.0000000001更圆。浮点运算不是随机数生成器。@EricPostchil我同意。我不是这么说的。我想说的是,舍入可以将一个如果不是舍入的话会是25的值变成24.999997或25.000001。浮点运算可以是随机数生成器,因为重复完全相同的运算不能保证产生完全相同的结果