/ fp的奇怪结果:快

时间:2010-06-15 06:16:06

标签: c++ visual-studio-2008 floating-accuracy compiler-optimization

我们有一些看起来像这样的代码:

inline int calc_something(double x) {
  if (x > 0.0) {
    // do something
    return 1;
  } else {
    // do something else
    return 0;
  }
}

不幸的是,当使用标记/fp:fast时,我们得到calc_something(0)==1,因此我们显然采用了错误的代码路径。这只发生在我们在代码中使用不同参数的多个点处使用该方法时,所以我认为编译器(Microsoft Visual Studio 2008,SP1)中存在一些可疑的优化。

此外,当我们将界面更改为

时,上述问题就消失了
inline int calc_something(const double& x) {

但我不知道为什么这会解决这个奇怪的行为。任何人都可以解释这种行为吗?如果我无法理解发生了什么,我们将不得不删除/fp:fast开关,但这会使我们的应用程序变慢。

5 个答案:

答案 0 :(得分:6)

我对FPU不够熟悉,无法确定地发表评论,但我的猜测是编译器会让它认为应该等于x的现有值参与该比较。也许你去了y = x + 20.; y = y - 20; y已经在FP堆栈上,因此编译器只需与x进行比较而不是加载y。但是由于四舍五入的错误,y并不像预期的那样0.0,你会看到奇怪的结果。

有关更好的解释:来自C ++ FAQ lite的Why is cos(x) != cos(y) even though x == y?。这是我想要解决的问题的一部分,我只是记不清楚到目前为止我在哪里阅读它。

更改为const引用可以解决此问题,因为编译器担心别名。它强制来自x的加载,因为在创建y后,它无法假设其值在某个时刻未发生变化,并且x实际上恰好是0.0 [在我熟悉的每种浮点格式中都可以表示]舍入错误消失了。

我非常确定MS提供了一个pragma,允许您在每个函数的基础上设置FP标志。或者您可以将此例程移动到单独的文件并为该文件提供自定义标志。无论哪种方式,它都可以防止你的整个程序遭受痛苦,只是为了保持一个例程的快乐。

答案 1 :(得分:3)

正如我在其他问题中所说,编译器很难生成浮点代码。丹尼斯链接的文章很好地解释了这些问题。这是另一个:An MSDN article

如果代码的性能很重要,您可以通过编写自己的汇编代码轻松地 1 执行编译器。如果您的算法是可矢量化的,那么您也可以使用SIMD(尽管精度略有下降)。

  1. 假设您了解FPU的工作方式。

答案 2 :(得分:2)

calc_something(0L)calc_something(0.0f)的结果是什么?它可以在转换之前链接到类型的大小。整数是4个字节,双精度是8。

您是否尝试查看过程代码,看看上述转换是如何完成的?

谷歌搜索'fp fast',我发现this post [social.msdn.microsoft.com]

答案 3 :(得分:2)

inline int calc_something(double x)将(可能)使用80位寄存器。 inline int calc_something(const double& x)会将double存储在内存中,占用64位。这至少解释了两者之间的差异。

然而,我发现你的测试开始时非常可疑。 calc_something的结果对其输入的舍入非常敏感。您的FP算法应该对舍入很稳健。 calc_something(1.0-(1.0/3.0)*3)应与calc_something(0.0)相同。

答案 4 :(得分:1)

我认为行为是正确的。

您永远不会将浮点数与保持类型的精度进行比较。

来自零的东西可能等于,大于或小于另一个零。

请参阅http://floating-point-gui.de/