浮動小数点演算の問題に対処しようとしているときに、少し紛らわしいことに遭遇しました。
まず、コード。私は自分の問題の本質をこの例に蒸留しました:
#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が0に評価されることについて尋ねるこの関連する質問を見ましたsqrt(3)
が、ここには関数呼び出しが含まれていないため、これは同じことではありません。