Я столкнулся с чем-то немного запутанным, пытаясь справиться с арифметической проблемой с плавающей запятой.
Во-первых, код. Я отвлек сущность моей проблемы в этом примере:
#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), тогда как фактический код С++, который вычисляет то же самое в программе, дает 0.16249854626123725 (0x3fc4ccc09aeb769b). Таким образом, GDB должен использовать другую модель оценки для арифметики с плавающей запятой. Может ли кто-нибудь пролить свет на это? Как оценка GDB отличается от оценки моего процессора?
Я рассмотрел этот связанный вопрос, задав вопрос о том, что GDB оценивает sqrt(3)
на 0, но это не должно быть одним и тем же, потому что нет вызовов функций здесь.