x86和x64上pow的不同结果

Different results for pow on x86 and x64

本文关键字:结果 pow x64 x86      更新时间:2023-10-16

我想知道为什么在x86和x64上分别使用pow时会观察到不同的结果。在我们的应用程序中,我们控制浮点舍入模式,它在Windows和Linux 32位上都运行良好。

#include <cmath>
#include <cstdio>
#include <cfloat>
void checkEqual(double expected, double observed) {   
    if (expected == observed) {
        printf("Expected %.23f, got %.23fn", expected, observed);
    }
    else {
        printf("ERROR: Expected %.23f, got %.23fn", expected, observed);
    }
}
int main(int argc, char **argv) {   
    unsigned ret, tmp;
    _controlfp_s(&ret, 0, 0);
    _controlfp_s(&tmp, RC_DOWN, MCW_RC);
    checkEqual(2048.0, pow(2.0, 11.0));
    checkEqual(0.125, pow(2.0, -3.0));
    return 0;   
}

使用Visual Studio 2015编译和运行(2012给出了相同的结果)会给我以下输出

x86:

Expected 2048.00000000000000000000000, got 2048.00000000000000000000000
Expected 0.12500000000000000000000, got 0.12500000000000000000000

x64:

ERROR: Expected 2048.00000000000000000000000, got  2047.99999999999977262632456
ERROR: Expected 0.12500000000000000000000, got 0.12499999999999998612221

有人能解释其中的区别吗?我知道浮点计算本质上是不精确的,但对于这些特定的值,无论舍入模式如何,我都希望函数能产生相同的结果。


我对此进行了更多的研究,发现真正的问题不是pow是如何实现的,而是x86和x64硬件的不同,正如Hans Passant所建议的那样。

pow可能有许多不同的实现。例如,pow(x,y)就是exp(y*ln(x))。正如你所能想象的,即使xy是可精确表示的,exp(11.0*ln(2.0))也可能遭受内部舍入误差。

然而,用整数参数调用pow是很常见的,因此一些库为这些情况优化了路径。例如,pow(x,2*n)pow(x,n)的平方,而pow(x, 2n+1)x*pow(x, 2n)

您的x86和x64实现在这方面似乎有所不同。这是可能发生的。IEEE仅承诺+-*/和sqrt的精确结果。