4

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

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

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

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

inline int calc_something(const double& x) {

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

4

5 回答 5

6

我对 FPU 不够熟悉,无法确定地发表评论,但我的猜测是编译器会让它认为应该等于的现有值x参与该比较。也许你y = x + 20.; y = y - 20; y已经在 FP 堆栈上,所以与其加载x编译器,不如只与y. 但是由于舍入错误,它与预期的y不太0.0一样,并且您会看到奇怪的结果。

为了更好的解释:为什么 cos(x) != cos(y) 即使 x == y?来自 C++FAQ 精简版。这是我试图表达的一部分,直到现在我才记得我在哪里读过它。

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

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

于 2010-06-15T08:00:39.567 回答
3

正如我在其他问题中所说,编译器在生成浮点代码方面很糟糕。Dennis 链接到的文章很好地解释了这些问题。这是另一个:一篇 MSDN 文章

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

  1. 假设您了解 FPU 的工作方式。
于 2010-06-15T08:30:18.360 回答
2

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

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

谷歌搜索“快速 fp”,我发现了这篇文章[social.msdn.microsoft.com]

于 2010-06-15T07:35:44.563 回答
2

inline int calc_something(double x)将(可能)使用 80 位寄存器。inline int calc_something(const double& x)会将双精度数存储在内存中,它需要 64 位。这至少解释了两者之间的区别。

但是,我发现您的测试一开始就很可疑。的结果calc_something对其输入的四舍五入极为敏感。您的 FP 算法应该对舍入具有鲁棒性。calc_something(1.0-(1.0/3.0)*3)应该是一样的calc_something(0.0)

于 2010-06-15T08:53:11.433 回答
1

我认为这种行为是正确的。

您永远不会比较小于保持类型精度的浮点数。

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

http://floating-point-gui.de/

于 2010-06-15T06:21:41.240 回答