Search code examples
cgccfloating-pointfloating-point-exceptions

C: unordered floating-point comparison does not raise FE_INVALID


I've run into an issue with floating-point comparisons. When comparing a value with NaN using the < operator, I expect the FE_INVALID flag to be set. The < operator should raise the flag according to the C11 standard (and also according to IEEE-754):

The isless macro determines whether its first argument is less than its second argument. The value of isless(x, y) is always equal to (x) < (y); however, unlike (x) < (y), isless(x, y) does not raise the "invalid" floating-point exception when x and y are unordered.

Here is an example program to reproduce my issue:

#include <stdio.h>
#include <math.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON

int main()
{
    volatile float a = 12.0f;
    volatile float b = NAN;
    volatile int   c;

    feclearexcept(FE_ALL_EXCEPT);

    c = (a < b);

    if (fetestexcept(FE_INVALID))
        printf("FE_INVALID\n");
    else
        printf("Not invalid\n");

    return 0;
}

On my machine (Linux, march=broadwell) it returns "Not invalid". I compiled it using GCC v7.2.0, using the -std=c11 option (not using it didn't change anything in the result). The emitted x86 instruction is UCOMISS which is only raising exceptions for signalling NaNs - I would expect to see COMISS as that would raise the exception on all NaN comparisons, as NaNs are unordered no matter whether they're signalling or not.

Did I make a mistake in my code or forget some compiler option to make the behavior IEEE-compliant? Could it be a bug (or performance optimization) in the compiler to ignore the need to raise an exception here?


Solution

  • OK so I took the plunge and got the most recent GCC version (8.2) running.

    The code behaves as expected when compiled under GCC 8.2.0, so I'll assume the problem was a compiler bug. I didn't try other versions in between 7.2.0 and 8.2 to see at which point it started working, but using 8.2 is good enough for me.