isnan 不能与 `-Ofast` 标志一起正常工作

Posted

技术标签:

【中文标题】isnan 不能与 `-Ofast` 标志一起正常工作【英文标题】:isnan does not work correctly with `-Ofast` flags 【发布时间】:2017-12-07 20:43:17 【问题描述】:

考虑以下测试程序:

# 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,并且 pearson 系数对至少两个样本有意义。

当我这样编译时:

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 版,Intel i5 以 64 位模式运行

提前致谢

【问题讨论】:

“fast-math 可能导致依赖于数学函数的 IEEE 或 ISO 规则/规范的精确实现的程序的错误输出”,来自 ***.com/questions/15944614/… 在这两种情况下检查fpclassify(pearson_corr)的值。 【参考方案1】:

-Ofast 会激活 GCC 的 -ffast-math 模式,-ffast-math 会导致编译器生成假定 NaN(和 Inf)永远不会发生的代码。

因此,如果您需要使用 NaN 或 Inf,您不得使用-Ofast。没有解决方法。

无论如何,-O2 -march=native 为几乎所有程序提供更好的服务; -Ofast 开启了极其激进的内联和循环展开,根据我的经验,这几乎总是会破坏 I-cache 并使程序变慢

【讨论】:

同意:-O2 是最好的妥协。 -O3 / -Ofast 适合真正的玩家 :) 更好地手动优化代码,而不是依赖一些神奇的选项 @Jean-FrançoisFabre 可以-O3 应用于精心挑选的复杂程序源文件子集,即具有最热内循环的子集。诚然,人类组装向导通常可以做得更好,但他们很难获得,而且工作成本也不便宜。 :) 同意。更不用说代码变得不可移植了。并且有了所有这些流水线和缓存的东西,编译器通常知道得更好。 68000 天结束了(叹气:))【参考方案2】:

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

【讨论】:

【参考方案3】:

-Ofast

无视严格的标准合规性。 -Ofast 启用所有 -O3 优化。 它还启用了不适用于所有标准兼容的优化 程式。 开启 -ffast-math

在这种情况下,isnan 不起作用,这里有更好的解释 Mingw32 std::isnan with -ffast-math

(其中一个答案包含一个可行的实现,但也可以将isnan 包装在一个未使用-Ofast 编译的c 文件中)

【讨论】:

【参考方案4】:

您告诉 GCC 假设没有 NaN,所以它假设没有 NaN。如果你这样做,任何依赖于 NaN 的代码显然都不可靠。

来自docs:

-Ofast

无视严格的标准合规性。 -Ofast 启用所有 -O3 优化。它还支持并非对所有符合标准的程序都有效的优化。 它会打开 -ffast-math 和 Fortran 特定的 -fstack-arrays,除非指定了 -fmax-stack-var-size 和 -fno-protect-parens。

-ffast-math

设置选项 -fno-math-errno、-funsafe-math-optimizations、-ffinite-math-only、-fno-rounding-math、-fno-signaling-nans、-fcx -limited-range 和 -fexcess-precision=fast。 ...

-仅有限数学

允许优化浮点运算假设参数和结果不是 NaNs 或 +-Infs。

【讨论】:

【参考方案5】:

您可以使用此选项:-Ofast -fno-finite-math-only,因此isnan 仍然有效。

或者,如果您不想使用 -fno-finithe-math-only,但仍想检测 NaN 数字,您可以通过平台相关的方式来实现:实现您自己的 isnan 函数。

例如,如果您在 IEEE-754 平台上使用 32 位浮点数和 64 位双精度数,您可以这样做:

#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);

【讨论】:

以上是关于isnan 不能与 `-Ofast` 标志一起正常工作的主要内容,如果未能解决你的问题,请参考以下文章

SizeToFit() 不能与 \n 一起正常工作?

(Smooth)ScrollToPosition 不能与 RecyclerView 一起正常工作

UISlider 不能与 AVPlayer 一起正常工作

参数匹配不能与 NSubstitute 一起正常工作

DirtyForms 不能与 $.blockUI 一起正常工作

Laravel groupBy 不能与 PostgreSQL 一起正常工作