Home > Software design >  Is it considered normal that f = NAN may cause raising floating-point exceptions?
Is it considered normal that f = NAN may cause raising floating-point exceptions?

Time:11-12

C2x (as well as previous):

The macro NAN is defined if and only if the implementation supports quiet NaNs for the float type. It expands to a constant expression of type float representing a quiet NaN.

Sample code (t0a.c)

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

#if _MSC_VER && ! __clang__ && ! __INTEL_COMPILER
#pragma fenv_access (on)
#else
#pragma STDC FENV_ACCESS ON
#endif

void print_fe_excepts_raised(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");
}

int main(void)
{
    float f;

    feclearexcept(FE_ALL_EXCEPT);
    f = NAN;
    print_fe_excepts_raised();
    (void)f;
    return 0;
}

Invocations:

# msvc (version 19.29.30133 for x64)
$ cl t0a.c /std:c11 /Za /fp:strict && t0a.exe
exceptions raised  FE_INEXACT FE_INVALID FE_OVERFLOW

# clang on Windows (version 13.0.0)
$ clang t0a.c -std=c11 -pedantic -Wall -Wextra -ffp-model=strict && ./a.exe
exceptions raised  FE_INEXACT FE_INVALID FE_OVERFLOW

# gcc on Windows (version 11.2.0)
$ gcc t0a.c -std=c11 -pedantic -Wall -Wextra  && ./a.exe
exceptions raised  none

# gcc on Linux (version 11.2.0)
$ gcc t0a.c -std=c11 -pedantic -Wall -Wextra  && ./a.out
exceptions raised  none

# clang on Linux (version 13.0.0)
$ clang t0a.c -std=c11 -pedantic -Wall -Wextra -ffp-model=strict && ./a.out
exceptions raised  none

For msvc and clang on Windows: this is because:

C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\corecrt_math.h:94:9
#define NAN ((float)(INFINITY * 0.0F))

C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\corecrt_math.h:90:9
#define INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF))

C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\corecrt_math.h:87:13
#define _HUGE_ENUF 1e 300 // _HUGE_ENUF*_HUGE_ENUF must overflow

Here we see that NAN "expands to a constant expression of type float representing a quiet NaN". Which implies that f = NAN may cause floating-point exceptions. However, f = NAN is usually seen as "writing to memory". Hence, people may wonder: "how writing to memory may cause raising floating-point exceptions?".

CodePudding user response:

For the record, this ...

The macro NAN is defined if and only if the implementation supports quiet NaNs for the float type. It expands to a constant expression of type float representing a quiet NaN.

... is C17 7.12/5, and it probably has the same or similar numbering in C2x.


Updated

The fact that when used with MSVC or Clang on Windows, your test program causes the FE_INVALID FP exception to be raised suggests that the combination of

  • Microsoft's C standard library and runtime environment with
  • the MSVC and Clang compilers and
  • the options you are specifying to those

is causing a signaling NaN to be generated and used as an arithmetic operand. I would agree that that is an unexpected result, probably indicating that these combinations fail to fully conform to the C language specification in this area.

has nothing to do with whether the resulting NaN is a quiet or a signaling one. The misconception there is that the FE_INVALID flag would be raised only as a consequence of generating or operating on a signaling NaN. That is not the case.

For one thing, IEEE-754 does not define any case in which a signaling NaN is generated. All defined operations that produce an NaN produce a quiet NaN, including operations in which one operand is a signaling NaN (so MSVC and Clang on Windows almost certainly do produce a quiet NaN as the value of the NAN macro). Most operations with at least one signaling NaN as an operand do, by default, cause the FE_INVALID flag to be raised, but that is not the usual reason for that flag to be raised.

Rather, under default exception handling, the FE_INVALID flag is raised simply because of a request to compute an operation with no defined result, such as infinity times 0. The result will be a quiet NaN. Note that this does not include operations with at least one NaN operand, which do have a defined result: a quiet NaN in many cases, unordered / false for comparisons, and other results in a few cases.

With that for context, it is important to recognize that just because NAN expands to a constant expression (in a conforming C implementation) does not mean that the value of that expression is computed at compile time. Indeed, given the specifications for MSVC's and Clang's strict fp modes, I would expect those modes to disable most, if not all, compile-time computation of FP expressions (or at minimum to propogate FP status flags as if the computations were performed at run time).

Thus, raising FE_INVALID is not necessarily an effect of the assignment in f = NAN. If (as in Microsoft's C standard library) NAN expands to an expression involving arithmetic operations then the exception should be raised as a result of the evaluating that expression, notwithstanding that the resulting NaN is quiet. At least in implementations that claim full conformance with IEC 60559 by defining the __STDC_IEC_559__ feature-test macro.

Therefore, although I will not dispute that

people may wonder: "how writing to memory may cause raising floating-point exceptions?".

, no convincing evidence has been presented to suggest that such causation has been observed.

Nevertheless, the value represented by a particular appearance of NAN in an expression that is evaluated has some kind of physical manifestation. It is plausible for that to be in an FPU register, and storing a signaling NaN from an FPU register to memory indeed could cause a FP exception to be raised on some architectures.

  • Related