cfloating-pointc11floating-point-exceptionsc-standard-library

QNAN passed into C standard library functions (ex. llrintf): not clear whether FP exceptions are raised or not


The macro NAN from math.h is quiet NAN:

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

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.

Quiet NaNs usually do not lead to raising of FP exceptions. Examples:

  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.

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

However:

  1. llrintf() is neither arithmetic operation, nor unordered-quiet predicates in Table 5.3. Hence, 5.2.4.2.2.3 and 5.11 are not applicable.
  2. 7.12.9.5 The lrint and llrint functions (for example) says nothing about whether FP exceptions are raised or not in case if input is quiet NaN.

Preliminary conclusion: Because of the general practice "quiet NaN does not lead to raising FP exceptions" it can be concluded that the lrint and llrint functions should not lead to raising FP exceptions if input is quiet NaN.

Practice:

Code (t125.c):

#include <fenv.h>
#include <math.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");
}

int main(void)
{
    long long ll;

    ll = llrintf(NAN);
    show_fe_exceptions();
    printf("ll %lld\n", ll);
    return 0;
}

Invocations:

$ cl t125.c /std:c11 /fp:strict && t125
exceptions raised:  FE_INEXACT FE_INVALID FE_OVERFLOW
ll 0

$ clang t125.c -std=c11 -ffp-model=strict -Wall -Wextra -pedantic && ./a.exe
t125.c:6:9: warning: unknown pragma ignored [-Wunknown-pragmas]
#pragma fenv_access (on)
        ^
1 warning generated.
exceptions raised:  FE_INEXACT FE_INVALID FE_OVERFLOW
ll -9223372036854775808

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

Question: can someone clarify the situation / behavior around raising of FP exceptions for C standard library functions (ex. llrintf) if input is quiet NaN?


Solution

  • The OP was under impression that term conversion implies only implicit conversion and explicit conversion (cast operation) (see C11 6.3 Conversions). Meaning that OP was under impression that term function is not a conversion.

    However, C11 F.3 Operators and functions (emphasis added) explicitly states that:

    The lrint and llrint functions in <math.h> provide the IEC 60559 conversions.

    Hence, yes, C11 F.4 Floating to integer conversion (emphasis added) answers the question:

    F.4 Floating to integer conversion

    1    ... Otherwise, if the floating value is infinite or NaN or if the integral part of the floating value exceeds the range of the integer type, then the ‘‘invalid’’ floating-point exception is raised and the resulting value is unspecified. ...