C++ 两种不同的编译器配置之间可能会丢失精度

C++ 两种不同的编译器配置之间可能会丢失精度,c++,floating-point,precision,C++,Floating Point,Precision,我目前在工作中遇到了一个问题,当编译器配置从调试更改为发行版时,可能会丢失精度,而发行版具有不同的优化级别。出于某种原因,在我们代码的其他地方,协方差矩阵(以及类似的东西)使用了非常大的值,这些值在1e90的某个地方。我遇到的问题是,每当计算中出现某种精度损失,而其中一个非常大的值仍然存在时,这两个值的乘积就会带来一些不稳定性。我不确定为什么没有使用更合理的值,但我不是编写此代码的人,所以是的。。。到目前为止,我相信我已经找到了问题的具体位置。我在该地点的确切数字如下所示: DBL sum =

我目前在工作中遇到了一个问题,当编译器配置从调试更改为发行版时,可能会丢失精度,而发行版具有不同的优化级别。出于某种原因,在我们代码的其他地方,协方差矩阵(以及类似的东西)使用了非常大的值,这些值在1e90的某个地方。我遇到的问题是,每当计算中出现某种精度损失,而其中一个非常大的值仍然存在时,这两个值的乘积就会带来一些不稳定性。我不确定为什么没有使用更合理的值,但我不是编写此代码的人,所以是的。。。到目前为止,我相信我已经找到了问题的具体位置。我在该地点的确切数字如下所示:

DBL sum = 6.000000040000000400e-004; // same for debug and release configurations
const DBL dinv = 2.000000020000000300e-004; // same for debug and release configurations
请注意,DBL是您的普通双精度:

typedef double DBL;
然后,执行以下操作:

sum /= dinv;
这将产生:

sum = 2.999999990000000100e+000 // (for debug configuration)<br>
sum = 2.999999989999999600e+000 // (for release configuration)
我从来没有真正阅读过反汇编,但我的理解如下:sum被移动到xmm0,然后xmm0被dinv(结果是xmm0,因为除法已经到位),然后xmm0被移动到sum

正如预期的那样,用于释放的拆解是不同的

--释放--

将总和分配给dinv的分解为:

1D7B55B7  movsd       xmm1,mmword ptr [esp+68h]  
我认为dinv是由[esp+68h]表示的指针指向的值,sum是由[esp+50h]表示的指针指向的值,这是正确的吗?如果没有,情况如何

有人知道我为什么会失去准确性吗?
xorps
的目的是什么

此链接中的x86指令集参考可能会有所帮助:

--更新--
正如下面提到的答案,调试配置使用/fp:precise,发布配置使用/fp:fast(使用Microsoft Visual Studio 2013,要获得项目的生成配置设置,只需右键单击该项目,单击属性,然后导航到C/C++)。对我来说,这导致1e-15订单的四舍五入错误,给出或接受订单。这对我来说是个问题,因为在代码的其他地方,有些人使用了非常大的值(在1e90的顺序上,给出或接受一个命令)。出于测试目的,我“中断”调试配置的一件事是将
sum/=dinv
计算分为两个步骤。首先,通过计算
1.0/dinv
(在下面的回答中提到这是一个错误的操作),取
dinv
的倒数,将该结果乘以
sum
,并将结果放入
sum
。我发现当我这样做时,调试和发布都表现得很糟糕。

如果您使用

  • GCC带有(直接或间接通过
    -funsafe数学优化
    -ffast数学
    -Ofast
  • Visual Studio
编译器可以在调试模式下生成标准除法指令:

1D91FF78  divsd       xmm0,mmword ptr [dinv]
或释放模式下的“乘反除法”:

1D7557B4  mulsd       xmm1,mmword ptr [esp+68h]
数学上

a / b = a * (1 / b)

但在现实世界中,乘以倒数总是会引入更多的错误,编译器不允许执行此优化,因为结果会不同且不一致(wrt IEEE-754)。

感谢编辑,我花了几分钟试图使其正确,但它不断给我错误。:-)Xorps就是XOR。所以它基本上是清除xmm0寄存器。DBL的定义是什么?对不起,我忘了指定DBL是什么<代码>类型定义双DBL如果您使用的是gcc:-Ofast,则忽略严格的标准遵从性-Ofast支持所有O3优化。它还支持对所有标准兼容程序无效的优化。它打开-ffast math和Fortran专用-fno保护parens和-fstack-array。谢谢您的回答。我查看了编译器设置,发现调试配置选择了/fp:precise,发布配置选择了/fp:fast。我测试的另一件事是,将
sum/=dinv
操作分为两个步骤来执行,以使自己确信精度损失是问题所在。首先,我计算
1.0/dinv
,然后将结果乘以
sum
。当我这样做的时候,调试和发布配置彼此更加一致,而且都是错误的(因为我在描述中提到了非常大的值)。
1D7557B4  mulsd       xmm1,mmword ptr [esp+68h]
a / b = a * (1 / b)