为什么在赋值后似乎改变了双倍的价值?

时间:2014-02-23 03:24:22

标签: c++ compiler-construction architecture

以下程序的结果在我的机器上对我来说有点奇怪。

#include <iostream>

using namespace std;

int main(){
    double a = 20;
    double b = 0.020;
    double c = 1000.0;

    double d = b * c;

    if(a < b * c)
        cout << "a < b * c" << endl;

    if(a < d)
        cout << "a < d" << endl;

    return 0;
}

输出:

$ ./test
a < b * c

我知道双精度因为精确而不准确。但我不希望这个值发生变化,并给出不一致的比较结果。

如果打印出a < b * c,我确实也应该打印a < d。但是当我在我的i686服务器甚至我的cygwin上运行此代码时。我可以看到a < b * c但看不到a < d

此问题已被确认为依赖于平台。这是由双重赋值的不同指令和实现引起的吗?

更新

生成的程序集:

main:
.LFB1482:
    pushl   %ebp
.LCFI0:
    movl    %esp, %ebp
.LCFI1:
    subl    $56, %esp
.LCFI2:
    andl    $-16, %esp
    movl    $0, %eax
    subl    %eax, %esp
    movl    $0, -8(%ebp)
    movl    $1077149696, -4(%ebp)
    movl    $1202590843, -16(%ebp)
    movl    $1066695393, -12(%ebp)
    movl    $0, -24(%ebp)
    movl    $1083129856, -20(%ebp)
    fldl    -16(%ebp)
    fmull   -24(%ebp)
    fstpl   -32(%ebp)
    fldl    -16(%ebp)
    fmull   -24(%ebp)
    fldl    -8(%ebp)
    fxch    %st(1)
    fucompp
    fnstsw  %ax
    sahf
    ja  .L3
    jmp .L2

    //.L3 will call stdout

3 个答案:

答案 0 :(得分:5)

假设:您可能会看到80位英特尔FPU的影响。

使用定义double d = b * c,数量b * c以80位精度计算,并在存储到d时四舍五入为64位。 (a < d)会将64位a与64位d进行比较。

OTOH,表达式为(a < b * c),在离开FPU之前,您将80位算术结果b * c直接与a进行比较。因此,b*c结果永远不会通过保存在64位变量中来剪切其精度。

您必须查看生成的指令以确定,我希望这会因编译器版本和优化程序标志而异。

答案 1 :(得分:3)

我不确定AS3机器的硬件类型,但是,例如,您可以在内部浮点单元使用大于64位浮点数来存储中间结果的机器中看到此行为。在具有x87浮点单元的x86 arch中就是这种情况(但不是SSE)。

问题是处理器会将bc加载到浮点寄存器,然后进行乘法运算并将临时结果存储在寄存器中。如果该寄存器大于64位,则结果将不同于d(或a),它们被计算并存储回存储器,强制它们为64位。

这是许多情况中的一种情况,您需要查看汇编代码以确定究竟发生了什么。您还需要了解硬件如何在内部处理浮点计算。

答案 2 :(得分:1)

在我的Windows机器上使用MinGW快速测试代码会产生完全相同的结果。但真正奇怪的是,如果我将双打更改为浮点数,那么一切都运行得很好(根本没有输出)。但是,如果我将它们更改为长双打,则会出现“a&lt; b * c”和“a&lt; d”。

我的猜测可能是因为双精度应该允许更高的精度,在将两个立即值相乘并进行比较时会出现一些奇怪的现象,而不是将结果存储起来以后再进行?这也可以解释为什么最终问题也出现了长双打,因为它们需要更多的内存空间。