Art*_*iuk 9 c++ floating-point d
我有两个类似的程序,一个用C++编写,另一个用D编写.
编译在Windows7 64位,64位二进制文件上.
C++版本,VS 2013:
#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
float eps = 1.0f;
float f = 0.0f;
while (f + eps != f)
f += 1.0f;
std::cout << "eps = " + std::to_string(eps) + ", max_f = " + std::to_string(f) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
D版,DMD v2.066.1:
import std.stdio;
import std.conv;
int main(string[] argv)
{
float eps = 1.0f;
float f = 0.0f;
while (f + eps != f)
f += 1.0f;
writeln("eps = " ~ to!string(eps) ~ ", max_f = " ~ to!string(f));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
C++版本按预期工作,当f = 16777216时发现f + e == f.
但D版永远挂了.当我放置断点时,我看到D版本f也是16777216(运行一段时间后)和Watch窗口(我使用VisualD)显示(f + e!= f)是'false'所以循环应该终止但它是不是运行时的情况.
我认为汇编可以给出答案,但我不是很好.
我是D的新手,所以应该是我滥用语言/编译器的情况(使用DMD编译,就像'dmd test.d'没有附加选项一样,也使用带有VisualD的VS和默认选项).任何想法D版本的程序可能有什么问题?谢谢!
拆卸:
C++:
000000013F7D1410 mov rax,rsp
000000013F7D1413 push rbp
000000013F7D1414 lea rbp,[rax-5Fh]
000000013F7D1418 sub rsp,0E0h
000000013F7D141F mov qword ptr [rbp+17h],0FFFFFFFFFFFFFFFEh
000000013F7D1427 mov qword ptr [rax+8],rbx
000000013F7D142B movaps xmmword ptr [rax-18h],xmm6
000000013F7D142F xorps xmm1,xmm1
float eps = 1.0f;
float f = 0.0f;
000000013F7D1432 movss xmm6,dword ptr [__real@3f800000 (013F7D67E8h)]
000000013F7D143A nop word ptr [rax+rax]
f += 1.0f;
000000013F7D1440 addss xmm1,xmm6
while (f + eps != f)
000000013F7D1444 movaps xmm0,xmm1
000000013F7D1447 addss xmm0,xmm6
000000013F7D144B ucomiss xmm0,xmm1
000000013F7D144E jp main+30h (013F7D1440h)
000000013F7D1450 jne main+30h (013F7D1440h)
Run Code Online (Sandbox Code Playgroud)
d:
000000013F761002 mov ebp,esp
000000013F761004 sub rsp,50h
{
float eps = 1.0f;
000000013F761008 xor eax,eax
000000013F76100A mov dword ptr [rbp-50h],eax
000000013F76100D movss xmm0,dword ptr [rbp-50h]
000000013F761012 movss dword ptr [f],xmm0
float f = 0.0f;
while (f + eps != f)
f += 1.0f;
000000013F761017 movss xmm1,dword ptr [__NULL_IMPORT_DESCRIPTOR+1138h (013F7C3040h)]
000000013F76101F movss xmm2,dword ptr [f]
000000013F761024 addss xmm2,xmm1
000000013F761028 movss dword ptr [f],xmm2
000000013F76102D fld dword ptr [f]
000000013F761030 fadd dword ptr [__NULL_IMPORT_DESCRIPTOR+1138h (013F7C3040h)]
000000013F761036 fld dword ptr [f]
000000013F761039 fucomip st,st(1)
000000013F76103B fstp st(0)
000000013F76103D jne D main+17h (013F761017h)
000000013F76103F jp D main+17h (013F761017h)
Run Code Online (Sandbox Code Playgroud)
接受harold的回答,程序行为是由混合的FPU和SSE使用引起的.
以下是D汇编代码段中的内容摘要.事实上,循环将永远运行.
当f达到16777216.0时,SSE严格按照IEEE-754行为,我们在这个值(f + = 1.0f)上加1.0,我们仍然在xmm2寄存器中获得16777216.0,然后我们将它存储到内存中.
(f + eps!= f)表达式在FPU上计算.由于FPU寄存器具有足够的精度(f + eps),因此结果为16777217.0.如果我们将这个结果存储回内存到float变量,那么我们得到期望值16777216.0(因为16777217.0不表示为float).并且(f + eps!= f)将为'false'并且循环将终止.但是我们不会将任何数字存储回内存并在FPU上执行比较(因为我们有两个操作数).这意味着我们比较一个严格根据IEEE-754(f)计算的数字和另一个用80位精度(f + eps)计算的数字.16777216.0!= 16777217.0并且循环永远运行.
我不是这方面的专家,但对我来说,看起来像SSE指令的浮点运算更强大,正如C++版本的程序所证明的那样.
我在D论坛上讨论过http://forum.dlang.org/thread/ucnayusylmpvkpcnbhgh@forum.dlang.org
事实证明,程序行为正确 - 根据语言规范,可以以更高的准确度执行中间计算.
任何D编译器的强大实现是:
import std.stdio;
int main()
{
const float eps = 1.0f;
const float step = 1.0;
float f = 0.0f;
float fPlusEps = f + eps;
while (f != fPlusEps)
{
f += step;
fPlusEps = f + eps;
}
writeln("eps = ", eps, ", max_f = ", f);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
混合FPU和SSE代码,这真的很奇怪.我认为绝对没有理由以这种方式实施它.
但他们有,并且结果是f + eps != f使用80位扩展精度
f += 1.0f评估,同时使用32位浮点数进行评估.
这意味着循环永远不会结束,因为f在达到
f + eps != f伪造的值(以80位精度,很大)之前将停止上升.
| 归档时间: |
|
| 查看次数: |
243 次 |
| 最近记录: |