浮点最大输出循环不';t终止于D,工作于C++; 我有两个类似的程序,一个是C++,另一个是D.< /P>

浮点最大输出循环不';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

编译是在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 = " + 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;
}