在C++中,添加双精度值会在不同的程序之间产生不同的结果



我有一个关于浮点加法的问题。我了解编译器和处理器体系结构如何产生浮点算术值。我在这里看到了很多与我的问题类似的问题,但它们都有一些变化,比如不同的编译器、不同的代码、不同的机器等。然而,当在两个不同的程序中以相同的方式在中添加双精度时,我遇到了一个问题,这两个程序用相同的参数调用相同的函数,导致了不同的结果。两个程序都是在同一台机器上用相同的编译器/标记编译的。代码看起来类似于此:

void function(double tx, double ty, double tz){
double answer;
double x,y;
x = y = answer = 0;
x = tx - ty;
y = ty - tz;
answer = (tx + ty + tz) * (x*y)
}

的值

tx,ty,tz

数量级为[10e-15,10e-30]。显然,这是我实际使用的函数的一个非常简化的版本,但是,对于两个程序,在同一台机器上,使用相同的编译器/标记,运行相同的浮点运算(而不仅仅是相同的函数,完全相同的代码),是否有可能为函数获得不同的结果?

一些可能性:

  1. function的源代码在两个程序中是相同的,但它出现在不同的上下文中,导致编译器以不同的方式编译它。例如,编译器可能会在一个地方而不是另一个地方内联它,并且由于在内联调用时与其他表达式组合,内联可能会导致表达式减少,因此会执行不同的运算。(要测试这一点,请将function移到一个单独的源文件中,单独编译它,并在不进行跨模块优化的情况下使用链接器进行链接。此外,请尝试在禁用优化的条件下进行编译。)

  2. 您认为function有相同的输入,因为它们在调试器中打印或查看时看起来是相同的,但实际上由于未打印的低位数字的微小差异而不同。(要测试这一点,请使用十六进制浮点格式打印完整值。为此,请在输出流中插入std::hexfloat,然后插入浮点值。或者,使用%a格式的Cprintf。)

  3. 程序中的其他内容会更改浮点状态,例如舍入模式。

  4. 您认为使用了相同的编译器、相同的源、相同的编译开关等等,但实际上并没有。

David Schwartz指出,浮点值在存储时可能会发生变化,就像它们只是溢出到堆栈时一样。之所以会出现这种情况,是因为一些处理器和C++实现可能在寄存器中存储精度较高的浮点值,但在内存中存储精度较低的浮点值。从技术上讲,这符合任何一个1。(名义上function内部的不同计算)或2。(传递给function的值不同),但它足够阴险,值得单独提及。

答案很简单。如果您的计算机表现出确定性,它将始终为相同的输入返回相同的结果。这是迄今为止编程语言背后的基本思想。(当然,除非我们谈论的是量子计算机。)

因此,问题归结为您是否真的有相同的输入。

尽管上面的函数看起来是严格的函数,但通常有隐藏的输入并不那么明显。例如,您可以在调用函数之前调整FPU的舍入模式。或者您可以设置不同的异常行为。在这两种情况下,函数对于某些输入可能表现不同。

因此,即使您的计算机不是非确定性的(即有缺陷),上述函数也可能返回不同的结果。尽管可能性不大。

最新更新