在尝试处理浮点算术问题时,我遇到了一些令人困惑的事情。
首先,代码。我将问题的本质提炼到了这个例子中:
#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;
}
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
您会注意到,如果我让 GDB 计算z.d * (1 - tau.d)
,它会给出 0.16249854626123722 (0x3fc4ccc09aeb769a),而在程序中计算相同内容的实际 C++ 代码会给出 0.16249854626123725 (0x3fc4ccc09aeb769b)。所以 GDB 必须对浮点运算使用不同的评估模型。任何人都可以对此有所了解吗?GDB 的评估与我的处理器的评估有何不同?
我确实看过这个相关的问题,询问 GDB 评估sqrt(3)
为 0,但这不应该是同一件事,因为这里不涉及函数调用。