C: unordered floating-point comparison does not raise FE_INVALID
Asked Answered
L

1

9

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?

Lees answered 7/2, 2019 at 11:9 Comment(4)
FWIW with MSVC the output is "FE_INVALID" and c is 0 although I commented out the pramga because it is not recognised.Photoemission
Corner: Insure feclearexcept(FE_ALL_EXCEPT); returns zero.Saltandpepper
Check __STDC_IEC_559__. "An implementation that defines _ STDC_IEC_559 _ shall conform to the specifications in this annex." (IEEE-compliant? ) If __STDC_IEC_559__ is not defined, code is not specified to behave as desired. Only mistake then is an incorrect expectation.Saltandpepper
@WeatherVane that's strange, but at least it works for you :) @chux just checked feclearexcept, it returns 0. Also very good point about IEEE-compliance. (Un)fortunately, __STDC_IEC_559__ is defined, so the issue lies somewhere else..Lees
L
3

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.

Lees answered 7/2, 2019 at 12:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.