cgccisnan

Why does `gcc -ffast-math` disable the correct result of `isnan()` and `isinf()`?


I understand that using the -ffast-math flag allows for unsafe math operations and disables signalling NaNs. However, I expected the functions isnan() and isinf() to still be able to return the correct results, which they do not.

Here's an example:

File test_isnan.c:

#include <stdio.h>
#include <math.h>

int main(void){

  /* Produce a NaN */
  const float my_nan = sqrtf(-1.f);
  /* Produce an inf */
  const float my_inf = 1.f/0.f;

  printf("This should be a NaN: %.6e\n", my_nan);
  printf("This should be inf: %.6e\n", my_inf);

  if (isnan(my_nan)) {
    printf("Caugth the nan!\n");
  } else {
    printf("isnan failed?\n");
  }

  if (isinf(my_inf)) {
    printf("Caugth the inf!\n");
  } else {
    printf("isinf failed?\n");
  }
}

Now let's compile and run the program without -ffast-math:

$ gcc test_isnan.c -lm -o test_isnan.o && ./test_isnan.o
This should be a NaN: -nan
This should be inf: inf
Caugth the nan!
Caugth the inf!

But with it:

$ gcc test_isnan.c -lm -o test_isnan.o -ffast-math && ./test_isnan.o
This should be a NaN: -nan
This should be inf: inf
isnan failed?
isinf failed?

So why don't isnan() and isinf() catch these nans and infs? What am I missing?

In case it might be relevant, here's my gcc version:

gcc (Spack GCC) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Solution

  • From https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html:

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

    Where:

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

    The moment you break that assumption, you can't expect those functions to work.

    I understand that you were hoping for this setting to optimize all the other operations while still providing a correct result for these two functions, but that's just not the way it works. I don't think there's a way to solve this. Maybe you can have a look at Clang, but I don't expect it to be different.