Napotkałem coś nieco mylącego podczas próby radzenia sobie z arytmetycznym problemem zmiennoprzecinkowym.Dlaczego GDB ocenia arytmetykę zmiennoprzecinkową w inny sposób niż C++?
Najpierw kod. Mam destylowana istotę mojego problemu w tym przykładzie:
#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
i xiden
powinny mieć taką samą wartość (gdy y == 1
), ale z powodu zmiennoprzecinkowej błąd zaokrąglenia nie. Ta część dostaję.
Pojawiło się pytanie, kiedy uruchomiłem ten kod (w rzeczywistości, mój prawdziwy program) za pośrednictwem GDB, aby wyśledzić rozbieżność. Jeśli używam GDB odtworzyć oceny dokonane w kodzie, to daje inny wynik dla 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
Zauważysz, że jeśli pytam GDB obliczyć z.d * (1 - tau.d)
, to daje 0.16249854626123722 (0x3fc4ccc09aeb769a), natomiast rzeczywisty kod C++, który oblicza to samo w programie, daje 0,16249854626123725 (0x3fc4ccc09aeb769b). Zatem GDB musi używać innego modelu oceny dla arytmetyki zmiennoprzecinkowej. Czy ktoś może rzucić więcej światła na ten temat? Czym różni się ocena GDB od oceny mojego procesora?
Spojrzałem na this related question pytając o GDB oceniając sqrt(3)
na 0, ale to nie powinno być to samo, ponieważ nie ma tu żadnych wywołań funkcji.
W rzeczywistości wynik gdb jest matematycznie poprawniejszy, więc wydaje się, że gdb używa większej precyzji FPU, podczas gdy g ++ prawdopodobnie używa instrukcji SSE. –