lrleon
lrleon

Reputation: 2648

isnan does not work correctly with `-Ofast` flags

Consider the following test program:

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

In some way, this code is some absurd, but its purpose is to show a subtle error that I am experiencing.

The call to gsl_stats_correlation() should give an error because the number of samples is 1 and the pearson coefficient has sense for at least two samples.

When I compile thus:

c++ test-r2.cc -lgsl -lgslcblas

the programs prints out -nan as result and the message "It is nan", which I consider correct because as I said it is not possible to compute the coefficient. The call to isnan()detects correctly that the result is nan. However, when I compile thus:

c++ -Ofast test-r2.cc -lgsl -lgslcblas

The program prints out -nan as result but the message "Not nan", which suggests that the call to isnan() was unable to detect the invalidity of pearson_corr variable.

So, my first question es "Why with the -Ofast flag the call to isnan() is not able to detect that the variable is nan. And my second question would be how to solve this problem in a way independent on the optimizations flags given to the compiler?

I am using gnu c++ version 5.4.0 on ubuntu 16.04 and an Intel i5 run at 64 bits mode

Thanks in advance

Upvotes: 3

Views: 2694

Answers (5)

geza
geza

Reputation: 29970

You can use this option: -Ofast -fno-finite-math-only, so isnan still works.

Or, if you don't want to use -fno-finithe-math-only, but still want to detect NaN numbers, you can do it in a platform dependent way: implement your own isnan function.

For example, if you're on a IEEE-754 platform with floats 32-bit and doubles 64-bit, you can do:

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

Upvotes: 2

Jean-Fran&#231;ois Fabre
Jean-Fran&#231;ois Fabre

Reputation: 140276

-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 in that case isnan doesn't work, as explained better here Mingw32 std::isnan with -ffast-math

(one of the answers contains an implementation which will work, but it's also possible to wrap isnan in a c file which isn't compiled with -Ofast)

Upvotes: 3

user2357112
user2357112

Reputation: 281683

You told GCC to assume no NaNs, so it's assuming no NaNs. Any code that depends on NaNs is obviously not going to be reliable if you do that.

From the docs:

  • -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 -fstack-arrays, unless -fmax-stack-var-size is specified, and -fno-protect-parens.

  • -ffast-math

    Sets the options -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans, -fcx-limited-range and -fexcess-precision=fast. ...

  • -ffinite-math-only

    Allow optimizations for floating-point arithmetic that assume that arguments and results are not NaNs or +-Infs.

Upvotes: 1

zwol
zwol

Reputation: 140786

-Ofast, among other things, activates GCC's -ffast-math mode, and -ffast-math, among other things, causes the compiler to generate code that assumes that NaN (and Inf) never occur.

Therefore, if you need to use NaN or Inf, you must not use -Ofast. There is no workaround.

Nearly all programs are better served by -O2 -march=native anyway; -Ofast turns on extremely aggressive inlining and loop unrolling that, in my experience, almost always blow the I-cache and make the program slower.

Upvotes: 13

Peter
Peter

Reputation: 14947

man gcc says this:

   -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.

and this:

   -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.

You are telling the compiler to ignore NaNs, among other strange species of a the floating-point family. If you don't want the compiler to do that, tell it -O2 or -O3 instead.

Upvotes: 4

Related Questions