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

标签 c++ floating-point precision

我目前在工作中遇到一个问题,当编译器配置从 Debug 更改为 Release 时可能会丢失精度,这具有不同的优化级别。出于某种原因,在我们代码的其他地方,极大的值被用于协方差矩阵(以及类似的东西),值在 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)

我查看了两种配置的反汇编,发现了一些差异(由于优化量不同,这是预期的)。

--调试--

1D91FF73  movsd       xmm0,mmword ptr [sum]
1D91FF78  divsd       xmm0,mmword ptr [dinv]
1D91FF7D  movsd       mmword ptr [sum],xmm0

我还没有真正读过反汇编,但我的理解是这样的:sum 被移动到 xmm0,然后 xmm0 被 dinv 就地除法(结果在 xmm0 中,因为就地除法),然后 xmm0 被移动到 sum .

不出所料,发布的反汇编是不同的。

--发布--

1D7557AB  movsd       xmm1,mmword ptr [esp+50h]  
1D7557B1  xorps       xmm0,xmm0  
1D7557B4  mulsd       xmm1,mmword ptr [esp+68h]  

将 sum 赋值给 dinv 的反汇编是:

1D7B55B7  movsd       xmm1,mmword ptr [esp+68h]  

我认为dinv是[esp+68h]所代表的指针所指向的值,sum是[esp+50h]所代表的指针所指向的值,我这样想对吗?如果不是,那是什么情况?

有人知道我为什么会失去精度吗? xorps 的用途是什么?

此链接中的 x86 指令集引用可能会有帮助:http://x86.renejeschke.de/

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

最佳答案

如果你正在使用

编译器可能会在 Debug模式下生成标准的除法指令:

1D91FF78  divsd       xmm0,mmword ptr [dinv]

或 Release模式下的“乘法逆除法”:

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

数学上的

a / b = a * (1 / b)

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

关于c++ - 两种不同的编译器配置之间可能会丢失精度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22531209/

相关文章:

C if 语句中的双重比较未按预期工作

c++ - boost :什么是 "convenience header"?

c++ - 字符串逆向程序抛出异常

floating-point - 在 Go 中获取机器 epsilon 的最简单方法

math - float 学有问题吗?

math - float 学坏了吗?

c++ - Linux 如何知道进程使用了​​多少物理内存?

c++ - cv::floodfill中实现了哪个算法

c - IEEE 754 : sqrtf() with fesetround(): different results between compilers: 0x42440a72 vs. 0x42440a73

math - OpenCL 向量类型是否使用 SIMD