我们有一些看起来像这样的代码:
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
开关,但这会使我们的应用程序变慢。
答案 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(尽管精度略有下降)。
答案 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)