2014-02-23 61 views
11

在我的机器上,以下程序的结果对我来说有点奇怪。为什么double的值似乎在赋值后被改变?

#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

此问题已被确认为与平台相关。这是由双重任务的不同指令和执行引起的吗?

UPDATE

生成的组件:

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 
+1

也许它是与不断优化。如果从'std :: cin'中抓住'a','b'和'c',你能显示程序集还是重现它? –

+0

没有来自编译器的错误/警告。 – StarPinkER

+0

我可以重现它如果我从std :: cin得到它,我会在稍后发布它。 @NateKohl – StarPinkER

回答

5

假设:你可以看到80位Intel FPU的影响。

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

OTOH,带有表达式(a < b * c),在离开FPU之前,您有一个80位算术结果b * ca直接进行比较。因此,b*c结果从未将其精度修改为保存在64位变量中。

你必须看看生成的指令是肯定的,我期望这将随编译器版本和优化器标志而变化。

+0

见http://stackoverflow.com/q/20869904/420683 – dyp

+0

我想在这里http://coliru.stacked-crooked.com/a/3022f77c07303e32设置的x87,但它并没有区别。 x87或SSE没有输出。这并不否定你所说的任何话,但我只是好奇我必须设置哪些编译器标志才能看到效果。 –

3

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

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

这是很多情况下,你需要看看你的汇编代码,以确定究竟是怎么回事。您还需要了解硬件在内部如何处理浮点计算。

+0

我认为你是对的。但是,这是否意味着比较a和中间结果时,中间值不会放到内存中,而是加载到fpu并在那里进行比较? – StarPinkER

1

在我的Windows机器上用MinGW对代码进行快速测试可以产生完全相同的结果。然而,真正奇怪的是,如果我将双打变成浮动,所有东西都应该完美地运行(根本没有输出)。但是,如果我将它们更改为长双打,则会显示“a < b * c”和“a < d”。

我的猜测是,也许因为双打都应该允许更多的精度,奇怪的事情是怎么回事相乘的两个直接的价值,做一个比较时上,与存储用于以后的结果?这也可以解释为什么最终这个问题会出现很长的双打,因为它们需要更多的内存空间。

相关问题