繁体   English   中英

为什么GDB评估浮点运算不同于C ++?

[英]Why does GDB evaluate floating-point arithmetic differently from C++?

在尝试处理浮点运算问题时,我遇到了一些有点混乱的问题。

一,代码。 我已经将我的问题的本质提炼到这个例子中:

#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;
}

xinumxiden应该具有相同的值(当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计算zd * (1 - tau.d) ,它给出0.16249854626123722(0x3fc4ccc09aeb769a),而在程序中计算相同内容的实际C ++代码给出0.16249854626123725(0x3fc4ccc09aeb769b)。 因此GDB必须使用不同的浮点运算评估模型。 任何人都可以对此有所了解吗? GDB的评估与我的处理器评估有何不同?

我确实看过这个相关的问题,询问GDB将sqrt(3)评估为0,但这不应该是一回事,因为这里没有涉及函数调用。

可能是因为x86 FPU在寄存器中以80位精度工作,但在将值存储到存储器时会舍入到64位。 GDB将在(解释)计算的每一步都存储到内存中。

GDB的运行时表达式评估系统肯定不能保证为您的浮点运算执行与编译器生成的优化和重新排序的机器代码相同的有效机器代码,以计算相同符号表达式的结果。 实际上,保证不执行相同的机器代码来计算给定表达式zd * (1 - tau.d) ,因为这可以被认为是在运行时执行隔离表达式求值的程序的子集以一种任意的,“符号正确”的方式。

由于优化(替换,重新排序,子表达式消除等),指令选择,选择,浮点代码生成和CPU实现其输出特别容易与其他实现(例如运行时表达式求值程序)的符号不一致寄存器分配和浮点环境。 如果你的代码片段在临时表达式中包含许多自动变量(正如你的那样),那么代码生成具有特别大的自由度甚至零优化传递,并且具有这种自由度 - 在这种情况下 - 会失去精度以最不重要的方式出现不一致的方式。

您不会深入了解为什么GDB的运行时评估程序执行它所做的任何指令而无需深入了解GDB源代码,构建设置以及它自己的编译时生成的代码。

您可以在生成的程序集中为您的程序达到峰值,以了解最终存储到ztau和[相反] xiden工作方式。 导致这些商店的浮点运算的数据流可能不像看起来那样。

更容易,通过禁用所有编译器优化(例如,GCC上的-O0 )并重写浮点表达式以不使用临时/自动变量,尝试使代码生成更具确定性。 然后打破GDB中的每一行并进行比较。

我希望我可以告诉你为什么尾数的最低有效位被翻转,但事实是,处理器甚至“不知道”为什么有些东西带有一些东西而其他东西没有因为,例如,顺序没有完整的指令和数据跟踪代码和GDB本身的评估。

它不是GDB与处理器,而是内存与处理器。 x64处理器存储的精度比实际存储的精度高(80ish对64位)。 只要它保留在CPU和寄存器中,它就会保留80位的准确度,但是当它被发送到内存时将确定何时以及它如何被舍入。 如果GDB将所有间歇计算结果发送出CPU(我不知道是否是这种情况,或者在任何地方接近),它将在一步都进行舍入,这会导致稍微不同的结果。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM