cfloating-pointieee-754floating-point-exceptionsc17

Why QNAN == QNAN does not lead to raising FE_INVALID exception?


Code (t125.c):

#include <fenv.h>
#include <stdint.h>
#include <stdio.h>

#if _MSC_VER
#pragma fenv_access (on)
#else
#pragma STDC FENV_ACCESS ON
#endif

void show_fe_exceptions(void)
{
    printf("exceptions raised: ");
    if (fetestexcept(FE_DIVBYZERO))     printf(" FE_DIVBYZERO");
    if (fetestexcept(FE_INEXACT))       printf(" FE_INEXACT");
    if (fetestexcept(FE_INVALID))       printf(" FE_INVALID");
    if (fetestexcept(FE_OVERFLOW))      printf(" FE_OVERFLOW");
    if (fetestexcept(FE_UNDERFLOW))     printf(" FE_UNDERFLOW");
    if (fetestexcept(FE_ALL_EXCEPT)==0) printf(" none");
    printf("\n");
}

typedef union { uint32_t u; float f; } ufloat;

int main(void)
{
    _Bool b;
    ufloat uqnan;
    volatile float f;

    uqnan.u = 0x7fc00000;
    f = uqnan.f;

    b = f == f;
    show_fe_exceptions();
    return b ? 1 : 0;
}

Invocations:

$ gcc t125.c -Wall -Wextra -pedantic -std=c17 && ./a.exe
t125.c:7: warning: ignoring ‘#pragma STDC FENV_ACCESS’ [-Wunknown-pragmas]
    7 | #pragma STDC FENV_ACCESS ON
      |
exceptions raised:  none

$ clang t125.c -Wall -Wextra -pedantic -std=c17 && ./a.exe
t125.c:7:14: warning: pragma STDC FENV_ACCESS ON is not supported, ignoring pragma [-Wunknown-pragmas]
#pragma STDC FENV_ACCESS ON
             ^
1 warning generated.
exceptions raised:  none

$ cl t125.c /fp:strict /std:c17 && t125
exceptions raised:  none

Question: why QNAN == QNAN does not lead to raising FE_INVALID exception?

UPD. Reason of the question: (false, see below) assumption that <any_NAN> == <any_NAN> leads to raising FE_INVALID exception.

UPD2. Changed code: from f = *(float*)&qnan to f = uqnan.f (type punning via union). This is to avoid violation of the aliasing rules of the C standard.


Solution

  • ISO/IEC 9899:2011 (E) (emphasis added):

    5.2.4.2.2 Characteristics of floating types <float.h>

    3    A quiet NaN propagates through almost every arithmetic operation without raising a floating-point exception; a signaling NaN generally raises a floating-point exception when occurring as an arithmetic operand.

    See also: What is the difference between quiet NaN and signaling NaN?.

    UPD. Yes, it seems that equality does not count as an arithmetic operation for this purpose. Then here is a quote from IEEE 754-2008 (emphasis added):

    5.11 Details of comparison predicates Programs that explicitly take account of the possibility of quiet NaN operands may use the unordered-quiet predicates in Table 5.3 which do not signal such an invalid operation exception.

    For example, the predicate LT EQ shall not lead to raising invalid operation exception. However, we see (in the comments above) that for f <= f both gcc and cl (msvc) raise FE_INVALID. Is it a bug / defect? Though none of them define __STDC_IEC_559__ with definition of 1. However, they are not required:

    __STDC_IEC_559__ The integer constant 1, intended to indicate conformance to the specifications in annex F (IEC 60559 floating-point arithmetic).