在处理一个浮点数算术问题时,我遇到了一些令人困惑的事情。
首先,这是代码。我将我的问题的本质浓缩成了这个例子:
#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,但这并不应该是同样的问题,因为这里没有涉及到函数调用。