为什么浮点运算的结果取决于将一个语句拆分为两个语句?

Why does the result of a floating point operation depend on splitting one statement into two?

提问人:Fabian 提问时间:7/8/2016 最后编辑:Fabian 更新时间:7/8/2016 访问量:98

问:

我偶然发现了一个问题,浮点运算的结果取决于我是现在减去一个值 () 还是以后减去一个值 ()。为什么?ff2

int main(int argc, char * argv[])
{   
    const float fResolution = 0.501f;
    const int iDiff = -6; // some values lead to f == f2, like -1, -2, -3, -4 or -5
    float f = iDiff * fResolution - 10.0f;
    float f2 = iDiff * fResolution;
    f2 -= 10.0f;

    printf("Difference %f %f %f: %d\n", f, f2, f - f2, iDiff);
    return 0;
}

输出:差值 -13.006000 -13.006001 0.000001:-6

我猜,它与浮点精度有关,但这里只有浮点数,那么和有什么区别呢?或者它可能是 Visual Studio 2010 中的特殊行为?ff2

这当然是一个最小的例子。在现实世界中,我把计算提取成一个方法,有时减去-10。由于上述问题,提取方法后我的程序的输出有所不同。iDiff * fResolution

反汇编(调试模式):

    const float fResolution = 0.501f;
00CC7872  fld         dword ptr [__real@3f004189 (0D4AEACh)]  
00CC7878  fstp        dword ptr [ebp-0F8h]  
    const int iDiff = -6;
00CC787E  mov         dword ptr [ebp-104h],0FFFFFFFAh  
    float f = iDiff * fResolution - 10.0f;
00CC7888  fld         dword ptr [ebp-0F8h]  
00CC788E  fmul        qword ptr [__real@c018000000000000 (0D4AED8h)]  
00CC7894  fsub        qword ptr [__real@4024000000000000 (0D4AE58h)]  
00CC789A  fstp        dword ptr [ebp-110h]  
    float f2 = iDiff * fResolution;
00CC78A0  fld         dword ptr [ebp-0F8h]  
00CC78A6  fmul        qword ptr [__real@c018000000000000 (0D4AED8h)]  
00CC78AC  fstp        dword ptr [ebp-11Ch]  
    f2 = f2 - 10.0f;
00CC78B2  fld         dword ptr [ebp-11Ch]  
00CC78B8  fsub        qword ptr [__real@4024000000000000 (0D4AE58h)]  
00CC78BE  fstp        dword ptr [ebp-11Ch] 
C++ 浮动精度

评论

2赞 Revolver_Ocelot 7/8/2016
中间计算可以以更精确的类型存储。当数据存储在变量中时,所有额外的精度都会丢失。或。如果编译器优化掉了某些变量,则最终值可能会再次更改。有趣的事实:可能会产生(在较旧的 GCC 编译器上)。sin(x) == sin(x)false
1赞 Cody Gray - on strike 7/8/2016
这可能与浮点值的中间存储有关。如果您以 x86-32 为目标,并告诉它精度不是最重要的,那么优化器可能会生成代码,在 80 位 FPU 堆栈上留下中间值,而不是将其溢出到内存中以保持恒定的精度。如果不看拆卸,就无法确定。这几乎不值得做,依靠浮点计算来获得精度是一件愚蠢的事情。
0赞 NathanOliver 7/8/2016
强制性插件:浮点数学是否被破坏? 浮点计算给出的结果与浮点计算不同(精度如何影响结果)
2赞 Hans Passant 7/8/2016
地址 00CC788E 处的指令结果以 64 位精度存储在 FPU 内。在地址 00CC78AC 处,强制将其存储回内存,将结果截断为 24 位精度。英特尔在 36 年前认为这是一个理想的功能,此后一大批程序员不同意。Fwiw,同样的事情也会发生在你的程序的发布版本中,但现在它不再由你控制。请更新您的 VS 版本,FPU 使用已在 VS2012 中停用。
0赞 Jesper Juhl 7/8/2016
相关阅读:docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

答: 暂无答案