以下程序的结果在我的机器上对我来说有点奇怪。
#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
答案 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)。
问题是处理器会将b
和c
加载到浮点寄存器,然后进行乘法运算并将临时结果存储在寄存器中。如果该寄存器大于64位,则结果将不同于d
(或a
),它们被计算并存储回存储器,强制它们为64位。
这是许多情况中的一种情况,您需要查看汇编代码以确定究竟发生了什么。您还需要了解硬件如何在内部处理浮点计算。
答案 2 :(得分:1)
在我的Windows机器上使用MinGW快速测试代码会产生完全相同的结果。但真正奇怪的是,如果我将双打更改为浮点数,那么一切都运行得很好(根本没有输出)。但是,如果我将它们更改为长双打,则会出现“a&lt; b * c”和“a&lt; d”。
我的猜测可能是因为双精度应该允许更高的精度,在将两个立即值相乘并进行比较时会出现一些奇怪的现象,而不是将结果存储起来以后再进行?这也可以解释为什么最终问题也出现了长双打,因为它们需要更多的内存空间。