1

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.

pmor
  • 5,392
  • 4
  • 17
  • 36
  • Can you say why you think this operation _should_ lead to the invalid flag being set? Admittedly, the relevant standards aren't _completely_ unambiguous here: assuming `__STDC_IEC_559__` is defined, annex F of the C standard just says "The relational and equality operators provide IEC 60559 comparisons", while IEC 60559 describes both "compareQuietEqual" and "compareSignalingEqual". IEC 60559 does however also say: "Language standards _should_ map their notations for the symbols = and ≠ to the Quiet predicates [...]". – Mark Dickinson Mar 24 '21 at 13:04
  • 1
    Because the Q in QNaN stands for Quiet. – Eric Postpischil Mar 24 '21 at 20:25
  • 2
    Do not write `*(float*)&`, because it violates the aliasing rules in the C standard. Use `float f; memcpy(&f, &qnan, sizeof f);` or `float f = (union { unsigned u; float f; }) {0x7fc00000} .f;`. – Eric Postpischil Mar 24 '21 at 20:27
  • What does your code give for `f <= f` in place of `f == f`? – Mark Dickinson Mar 24 '21 at 21:02
  • @EricPostpischil Correct, thanks. @Mark Dickinson Thanks for the idea. For `f <= f` the things become hot: `gcc: FE_INVALID, clang: none, cl (msvc): FE_INVALID`. Any explanations? – pmor Mar 24 '21 at 22:04
  • @MarkDickinson Though none of them define `__STDC_IEC_559__` with definition of 1. However, they are (seems) not _required_ to define `__STDC_IEC_559__` with definition of 1. See updated answer. – pmor Mar 24 '21 at 22:33
  • @EricPostpischil I thought the union trick was potentially undefined behavior these days too, and you had to use `memcpy` to be sure. – Mark Ransom Mar 24 '21 at 22:36
  • @MarkRansom Quote from comments to [this question](https://stackoverflow.com/q/64617357/1778275): _as per the C standard, type-punning through unions is totally ok. It's implementation defined, yes, but it should work_. – pmor Mar 24 '21 at 22:54
  • 1
    @MarkRansom: It is defined in C but not in C++. – Eric Postpischil Mar 24 '21 at 23:27
  • @EricPostpischil _Because the Q in QNaN stands for Quiet_: C standard explicitly talks about _arithmetic operations_, IEEE 754 explicitly talks about _unordered-quiet predicates in Table 5.3_. What about C standard library functions? Can we conclude / assume that `lrintf(NAN)` (for example) shall not lead to raising `FE_INVALID`? – pmor Mar 25 '21 at 12:33
  • @EricPostpischil Or is the behavior wrt raising `FE_INVALID` _unspecified_? – pmor Mar 25 '21 at 12:40

1 Answers1

1

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).

pmor
  • 5,392
  • 4
  • 17
  • 36
  • I'm not sure that equality counts as an arithmetic operation for this purpose. I'd also suspect that an order comparison operator involving a quiet NaN (e.g., `qnan <= qnan`) *should* signal the invalid operation exception, in which case the contents of 5.2.4.2.2 aren't quite enough here. – Mark Dickinson Mar 24 '21 at 21:10