我在尝试处理浮点算术问题时遇到了一些令人困惑的事情。
首先,代码。我将问题的本质提炼到这个例子中:
#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
and 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 的评估与我的处理器的评估有何不同?
我确实看过这个相关问题 https://stackoverflow.com/questions/5122570/why-does-gdb-evaluate-sqrt3-to-0询问GDB评估sqrt(3)
到 0,但这不应该是同一件事,因为这里不涉及函数调用。