C2x (as well as previous):
The macro
NAN
is defined if and only if the implementation supports quiet NaNs for thefloat
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 thefloat
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.