为什么相同的代码在不同的机器上产生两个不同的 fp 结果?

mar*_*zzz 4 c++ floating-point floating-accuracy

这是代码:

#include <iostream>
#include <math.h>

const double ln2per12 = log(2.0) / 12.0;

int main() {
    std::cout.precision(100);
    double target = 9.800000000000000710542735760100185871124267578125;
    double unnormalizatedValue = 9.79999999999063220457173883914947509765625;
    double ln2per12edValue = unnormalizatedValue * ln2per12;
    double errorLn2per12 = fabs(target - ln2per12edValue / ln2per12);
    std::cout << unnormalizatedValue << std::endl;
    std::cout << ln2per12 << std::endl;
    std::cout << errorLn2per12 << " <<<<< its different" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

如果我在我的机器上尝试 ( MSVC),或者在这里( GCC):

errorLn2per12 = 9.3702823278363212011754512786865234375e-12
Run Code Online (Sandbox Code Playgroud)

相反,这里GCC):

errorLn2per12 = 9.368505970996920950710773468017578125e-12
Run Code Online (Sandbox Code Playgroud)

这是不同的。是由于机器 Epsilon造成的吗?或者编译器精度标志?还是评价不同IEEE

造成这种漂移的原因是什么?问题似乎出在fabs()函数上(因为其他值似乎相同)。

Max*_*hof 6

即使没有,C++ 标准也不要求实现与(or 、or等)-Ofast精确,只是要求它们在几个 ulp 之内(即最后的二进制位置可能有一些不准确)。这允许更快的硬件(或软件)近似,每个平台/编译器可能会做不同的事情。logsinexp

唯一一个在所有平台上都能获得完美结果的浮点数学函数是sqrt

更烦人的是,您甚至可能在编译(编译器可能使用一些内部库来精确float/double允许常量表达式)和运行时(例如硬件支持的近似值)之间得到不同的结果。

如果您想log在跨平台和编译器上给出完全相同的结果,则必须仅使用+-*/sqrt或找到具有此保证的库)自己实现它。并在此过程中避免一大堆陷阱。

如果您一般需要浮点确定性,我强烈建议您阅读这篇文章以了解您面临的问题有多大:https ://randomascii.wordpress.com/2013/07/16/floating-point-determinism/