为什么GDB评估浮点运算不同于C++?

Dav*_*d Z 16 c++ floating-point gdb floating-accuracy

在尝试处理浮点运算问题时,我遇到了一些有点混乱的问题.

一,代码.我已经将我的问题的本质提炼到这个例子中:

#include <iostream>
#include <iomanip>

using namespace std;
typedef union {long long ll; double d;} bindouble;

int main(int argc, char** argv) {
    bindouble y, z, tau, xinum, xiden;
    y.d = 1.0d;
    z.ll = 0x3fc5f8e2f0686eee; // double 0.17165791262311053
    tau.ll = 0x3fab51c5e0bf9ef7; // double 0.053358253178712838
    // xinum = double 0.16249854626123722 (0x3fc4ccc09aeb769a)
    xinum.d = y.d * (z.d - tau.d) - tau.d * (z.d - 1);
    // xiden = double 0.16249854626123725 (0x3fc4ccc09aeb769b)
    xiden.d = z.d * (1 - tau.d);
    cout << hex << xinum.ll << endl << xiden.ll << endl;
}
Run Code Online (Sandbox Code Playgroud)

xinum并且xiden应该具有相同的值(何时y == 1),但由于浮点舍入错误,它们不会.那部分我得到了.

当我通过GDB运行此代码(实际上是我的真实程序)来追踪差异时,问题出现了.如果我使用GDB重现代码中完成的评估,它会为xiden提供不同的结果:

$ gdb mathtest
GNU gdb (Gentoo 7.5 p1) 7.5
...
This GDB was configured as "x86_64-pc-linux-gnu".
...
(gdb) break 16
Breakpoint 1 at 0x4008ef: file mathtest.cpp, line 16.
(gdb) run
Starting program: /home/diazona/tmp/mathtest 
...
Breakpoint 1, main (argc=1, argv=0x7fffffffd5f8) at mathtest.cpp:16
16          cout << hex << xinum.ll << endl << xiden.ll << endl;
(gdb) print xiden.d
$1 = 0.16249854626123725
(gdb) print z.d * (1 - tau.d)
$2 = 0.16249854626123722
Run Code Online (Sandbox Code Playgroud)

您会注意到,如果我要求GDB计算z.d * (1 - tau.d),它会给出0.16249854626123722(0x3fc4ccc09aeb769a),而在程序中计算相同内容的实际C++代码给出0.16249854626123725(0x3fc4ccc09aeb769b).因此GDB必须使用不同的浮点运算评估模型.任何人都可以对此有所了解吗?GDB的评估与我的处理器评估有何不同?

我确实看过这个相关的问题,询问GDB评估sqrt(3)为0,但这不应该是一回事,因为这里没有涉及函数调用.

Bry*_*yan 5

可能是因为x86 FPU在寄存器中以80位精度工作,但在将值存储到存储器时会舍入到64位.GDB将在(解释)计算的每一步都存储到内存中.


Moo*_*uck 2

这不是 GDB 与处理器的对比,而是内存与处理器的对比。x64 处理器存储的精度位数比内存实际容纳的位数多(80 位与 64 位)。只要它保留在 CPU 和寄存器中,它就会保留 80 位左右的精度,但是当它被发送到内存时,将决定它何时以及如何进行舍入。如果 GDB 将所有间歇性计算结果发送到 CPU 之外(我不知道是否是这种情况,或者是否接近),它会在一步进行舍入,这会导致结果略有不同。