考虑以下测试程序:
# include <gsl/gsl_statistics_double.h>
# include <iostream>
using namespace std;
int main()
{
double y = 50.2944, yc = 63.2128;
double pearson_corr = gsl_stats_correlation(&y, 1, &yc, 1, 1);
cout << "pearson_corr = " << pearson_corr << endl;
if (isnan(pearson_corr))
cout << "It is nan" << endl;
else
cout << "Not nan" << endl;
}
在某种程度上,这段代码有些荒谬,但它的目的是显示我遇到的一个微妙的错误。
对gsl_stats_correlation()
的调用应该给出错误,因为样本数为1,而皮尔逊系数对至少两个样本有意义。
当我编译时:
c++ test-r2.cc -lgsl -lgslcblas
程序打印出-nan
作为结果和消息“It is nan”,我认为这是正确的,因为我说不可能计算系数。对isnan()
的调用正确检测到结果为nan
。但是,当我编译时:
c++ -Ofast test-r2.cc -lgsl -lgslcblas
该程序打印出-nan
作为结果,但显示消息“Not nan”,这表示对isnan()
的调用无法检测到pearson_corr
变量无效。
所以,我的第一个问题是“为什么用-Ofast
标记调用isnan()
无法检测到变量是nan
。我的第二个问题是如何以独立于给编译器的优化标志的方式解决这个问题?
我在ubuntu 16.04上使用gnu c ++版本5.4.0,在64位模式下使用Intel i5
提前致谢
答案 0 :(得分:9)
-Ofast
激活GCC的-ffast-math
模式,-ffast-math
等导致编译器生成假定NaN(和Inf)永不发生的代码。
因此,如果您需要使用NaN或Inf,则不得使用-Ofast
。没有解决方法。
无论如何,-O2 -march=native
几乎所有节目都能提供更好的服务; -Ofast
打开非常积极的内联和循环展开,根据我的经验,几乎总是吹掉I-cache并使程序更慢。
答案 1 :(得分:3)
man gcc
说:
-Ofast
Disregard strict standards compliance. -Ofast enables all -O3 optimizations. It also enables optimizations that are not valid for all standard-compliant programs. It turns on -ffast-math
and the Fortran-specific -fno-protect-parens and -fstack-arrays.
和此:
-ffast-math
Sets -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and -fcx-limited-range.
This option causes the preprocessor macro "__FAST_MATH__" to be defined.
This option is not turned on by any -O option besides -Ofast since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications
for math functions. It may, however, yield faster code for programs that do not require the guarantees of these specifications.
您告诉编译器忽略NaN,以及浮点系列的其他奇怪物种。如果您不希望编译器这样做,请告诉它-O2
或-O3
。
答案 2 :(得分:2)
-Ofast
无视严格的标准合规性。 -Ofast启用所有-O3优化。 它还支持对所有符合标准的无效的优化 程式。 启用-ffast-math
并且在这种情况下isnan
不起作用,正如Mingw32 std::isnan with -ffast-math
(其中一个答案包含一个可行的实现,但也可以将isnan
包装在一个未使用-Ofast
编译的c文件中
答案 3 :(得分:1)
你告诉GCC假设没有NaN,所以假设没有NaN。如果你这样做,任何依赖于NaN的代码显然都不可靠。
来自docs:
-Ofast
无视严格的标准合规性。 -Ofast启用所有-O3优化。它还支持对所有符合标准的程序无效的优化。 它打开-ffast-math 和Fortran特定的-fstack-arrays,除非指定了-fmax-stack-var-size,并且-fno-protect-parens。
-ffast-数学
设置选项-fno-math-errno,-funsafe-math-optimizations, -ffinite-math-only ,-fno-rounding-math,-fno-signaling-nans,-fcx - 限制范围和-fexcess-precision =快速。 ...
-ffinite-数学仅
允许优化浮点运算,假设参数和结果不是NaN 或+ -Infs。
答案 4 :(得分:1)
您可以使用此选项:-Ofast -fno-finite-math-only
,因此isnan
仍有效。
或者,如果您不想使用-fno-finithe-math-only
,但仍想检测NaN号码,则可以依赖于平台的方式执行:实现您自己的isnan
功能。< / p>
例如,如果您使用32位浮点和64位双精度的IEEE-754平台,则可以执行以下操作:
#include <cstdint>
#include <string.h>
bool myIsnan(float v) {
std::uint32_t i;
memcpy(&i, &v, 4);
return ((i&0x7f800000)==0x7f800000)&&(i&0x7fffff);
}
bool myIsnan(double v) {
std::uint64_t i;
memcpy(&i, &v, 8);
return ((i&0x7ff0000000000000)==0x7ff0000000000000)&&(i&0xfffffffffffff);
}