浮点最大输出循环不';t终止于D,工作于C++; 我有两个类似的程序,一个是C++,另一个是D.< /P>
编译是在Windows7上进行的,从64位编译到64位二进制文件 C++版本,与2013年相比:浮点最大输出循环不';t终止于D,工作于C++; 我有两个类似的程序,一个是C++,另一个是D.< /P>,c++,floating-point,d,C++,Floating Point,D,编译是在Windows7上进行的,从64位编译到64位二进制文件 C++版本,与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
#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;
}
C++版本按预期工作,并在f=16777216时发现f+e==f
但D版永远都是。当我放置断点时,我看到在D版本中,f也是16777216(在运行一段时间之后),并且观察窗口(我使用VisualD)显示(f+e!=f)为“false”,所以循环应该终止,但在运行时不是这样
我想大会可以给出答案,但我不太擅长
我是D新手,所以应该是我误用了语言/编译器(使用DMD编译,就像使用“DMD test.D”一样,没有附加选项,也从VS使用VisualD和默认选项)。你知道D版的程序有什么问题吗?谢谢
拆卸:
C++:
D:
总结
接受harold的回答,即程序行为是由于FPU和SSE的混合使用
下面是D汇编代码段中发生的情况的摘要。事实上,循环将永远运行
SSE严格按照IEEE-754进行操作,当f达到16777216.0时,我们将该值加1.0(f+=1.0f),我们仍然在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论坛上进行了一次讨论
事实证明,该程序运行正确——根据语言规范,中间计算可以以更高的精度执行
任何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;
}
试图打破循环!=带浮点值的or==正在查找问题 不太可能出现不同的行为,因为向FPU传递值时,编译器可能采用浮点到双精度到80位的内部浮点转换 特别是在扩展尾数时——一些编译器或优化器可以决定让低有效位“随机”而不是归零。因此,
1.0f
,当给予FPU时,可能变成1.0000000000000000 12134432
,根据float
,精度仍然是1.0
,但FPU会比较1.0000000000000000000000 12134432
和1.0000000000000000000000000000 89544455
(两个尾部是随机的),看起来不一样
< >您应该验证C++和D编译器如何处理浮点扩展/还原,并最终配置适当的交换机:如果两个编译器不是来自同一个制造商,那么Tayy可能为它们各自的默认值做出了不同的选择。真奇怪。我认为绝对没有理由这样实施 但是他们有,结果是
f+eps!=f
以80位扩展精度进行评估,而f+=1.0f
使用32位浮点计算
这意味着循环永远不会结束,因为f
将在使f+eps!=f
false(在80位精度下,它是巨大的)。这可能与优化和浮点数学编译器选项有关。试着和他们一起玩?也尝试以不同的方式编写循环(只是为了确保任何优化不会中断它)。是的,也许DMD有一些不同的浮点默认值,但是我执行了非常简单的操作,并预期这将与C++中的工作相同。显示反汇编。特别是,其中一个真的使用浮点大小的浮点,而另一个使用80位扩展精度吗?需要很长时间(不是永远,而是太长)才能以80位的扩展精度跳出这个循环。只需再加几个音符。我在这里测试的是IEEE-754规范。对于任何实现,它都应该正常工作。80位转换这是x86的唯一实现细节。当我们在内存中得到结果时,它应该符合规范。我认为我们不应该对如此低级的细节考虑太多。是的,我同意我们不应该将FP计算结果与==或!=进行比较但在本例中,我正在根据IEEE-754测试浮点行为,因此我相信它在这种情况下是有效的。不幸的是,编译器通常不这样工作,它们将继续使用80位精度,而不需要额外的步骤将结果切回其正确的精度。正如您所说,当您将其返回内存时,它应该符合规范,但您可能无法将其返回内存,它可能会作为优化保留在寄存器中。无论如何,这很容易检查:请参阅反汇编。@harold:为了使代码运行更快:FPU在内部使用80位表示。如果你提供的更少,并且不重置尾部,你需要更少的CPU和FPU之间的通信带宽。如果你做了一系列的FADD和FMUL,你迟早会放弃“精确结果竞技场”。它使用的是==(或!=)真正的“麻烦”。如果我必须设计一种新的语言
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)
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)
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;
}