WG14 Document: N1011 (update of N1000)
Date: 2003-04-04
Title: Optional support for Signaling NaNs



Optional support for Signaling NaNs

The following was adopted from NCEG 91-028 (Floating-Point C Extensions), the last NCEG (Numerical C Extensions Group) document to discuss support for Signaling NaNs.

The existing binary floating-point arithmetic standard IEC-60559 (International version of ANSI IEEE-754) requires two kinds of NaNs: Quiet NaNs and Signaling NaNs. As IEEE-754 is being revised in the year 200x, there is a strong push to make Signaling NaNs optional and recommend against them. Standard C, in 1999, only adopted Quiet NaNs. It did not adopt Signaling NaNs because it was believed that they are of too limited utility for the amount of work required (and most committee members still believe that). The two uses of signaling NaNs that the committee knows about (use of a signaling NaN causes a trap handler to be called (this works if the raise of a floating-point exception is turned into a signal of SIGFPE), and to "initialize" storage to signaling NaNs so as to catch the use of an uninitialized variable (this works best if the all 1s bit pattern is a signaling NaN, as that would work for float, double, and long double encodings)) are allowed, but not required, so are not portable.

But, for implementations that do wish to support Signaling NaNs, it is highly recommended that they be supported in the following manner (to promote consistency in implementations and portability among applications).

For this to work, there are several requirements on both applications and implementations. The application needs to be able to determine if the implementation supports signaling NaNs. The application needs to indicate to the implementation that it wishes to use signaling NaNs. The implementation needs a way to determine, both at translation time and runtime, if support for signaling NaNs is being requested.

An application indicates that it wants to use signaling NaNs by defining the macro symbol _WANT_SNAN. If defined, _WANT_SNAN shall be defined before any Standard C headers are included. Behavior is undefined if _WANT_SNAN is defined after any Standard C headers are included. Behavior is undefined if _WANT_SNAN is both defined and undef'ed in a translation unit. An application that is made up of two or more translation units (whose behaviour depends upon _WANT_SNAN) shall be consistent in its definition of _WANT_SNAN: either all translation units define _WANT_SNAN, or none of the translation units define _WANT_SNAN; otherwise, the results are undefined.

The above addresses the compiler's own standard libraries and the user's source (translation units), but ignores other libraries, so called third party libraries. It is (third party) implementaiton defined as to what happens when their libraries are used and the use of _WANT_SNAN differs between the library and the user's source. If a third party library works the same, whether _WANT_SNAN is defined or not when that library was built, then it is as if there are two versions of the library and the appropriate version is used with the user's program.

An implementation indicates that it supports signaling NaNs by predefining the macro symbol __SUPPORT_SNAN__.

It is assumed that if both _WANT_SNAN and __SUPPORT_SNAN__ are defined, the implementation will do some magic (such as define an external variable in the implementation's namespace, or link edit with a signaling NaN aware library), so that the runtime functions fprintf, fscanf, strtod, and related functions (including the wide character equivalents), know if support for signaling NaNs has been requested. That is, implementations that support signaling NaNs will extend those functions to test how signaling NaNs shall be treated at runtime (either as an extension or as an error).

Binary data files that contain signaling NaNs are not portable, since there is no standard encoding of NaNs (of either flavor) in IEEE-754. So, even quiet NaNs on one platform might be a signaling NaN on another platform.

5.2.4.2.2 Characteristics of floating types <float.h>

Floating types may support not only numeric values, finite and possibly infinite, but also NaN (Not-a-Number) values, which do not represent numbers. A NaN that generally raises an exception when encountered as an operand of arithmetic operations is called a signaling NaN; the operation is said to trigger the signaling NaN. A NaN that behaves predictably and does not raise exceptions in arithmetic operations is called a quiet NaN. The IEEE floating-point standards (of the 1980s) specify quiet and signaling NaNs, but this document applies the general terms for non-IEEE implementations as well -- for example, the VAX reserved operand and the CDC and CRAY indefinite qualify as signaling NaNs. In IEEE standard arithmetic, operations that trigger a signaling NaN argument generally return a quiet NaN result, provided no trap is taken.

The primary utility of quiet NaNs -- "to handle otherwise intractable situations, such as providing a default value for 0.0/0.0" -- can be well supported through straightforward extensions to Standard C.

Other applications of NaNs may prove useful. Available parts of NaNs have been used to encode auxiliary information, for example about the NaN's origin. Signaling NaNs are good candidates for filling uninitialized storage; and their available parts could distinguish uninitialized floating objects. IEEE signaling NaNs and trap handlers potentially provide hooks for maintaining diagnostic information or for implementing special arithmetics.

However, C support for signaling NaNs, or for auxiliary information that could be encoded in NaNs, is problematic. Implementation mechanisms may trigger signaling NaNs, or fail to, in mysterious ways. The IEEE floating-point standards require that NaNs propagate, but not all implementations faithfully propagate the entire contents. And even the IEEE standards fail to specify the contents of NaNs through format conversion, which is pervasive in some C implementation mechanisms.

Whether an operation that merely returns the value of a numeric operand, changing at most its sign, triggers signaling NaNs is unspecified. Such operations include conversions that do not change precision, the unary + and - operators, and the fabs and copysign functions.

Leaving the semantics unspecified allows more efficient implementation. The IEEE floating-point standards explicitly give the option to implement same-precision conversions by either arithmetic operations (which trigger) or data moves (which do not trigger), which is important because of the frequency of such conversions in assignments, argument passing, function return, and initialization.

Binary operations that have a normal operand and a quiet NaN operand should produce the same quiet NaN as a result. Binary operations that have a normal operand and a signaling NaN operand shall trigger the signaling NaN and should produce that signaling NaN made quiet as a result. Binary operations that have two quiet NaN operands should produce a result that is one of the two quiet NaNs. Binary operations that have two signaling NaN operands trigger both signaling NaNs and should produce one of those signaling NaNs made quiet as a result.

6.7.8 Initialization

The signaling NaN macros: NANSF, NANS, NANSL, NANSCF, NANSC, NANSCL, NANSIF, NANSI, and NANSIL, when used to initialize a static object of the same type or precision as themselves, shall not trigger; when used to initialize an automatic or allocated object of the same type or precision as themselves, they might trigger; otherwise, they shall trigger. The 'same precision' options are to allow for implementations that map different types, such as double and long double, to the same hardware representation, hence same precision, to treat them as equivalent types and not require the signaling NaNs to trigger. Of course, portable programs will not rely upon mixed types not triggering.

6.10.8 Predefined macro names

The predefined macro names are augmented with

        __SUPPORT_SNAN__

which has the value integer constant 1, intended to indicate conformance to these specifications (optional support for Signaling NaNs).

7.3.1 Introduction to complex arithmetic <complex.h>

The macros

       NANSCF
       NANSC
       NANSCL
       NANSIF
       NANSI
       NANSIL

are defined if and only if the implementation supports signaling NaNs for the float complex, double complex, long double complex, float imaginary, double imaginary, and long double imaginary types, respectively. If defined, they each expand to a constant expression, of their respective type, representing a signaling NaN. They shall be usable in initialization.

For the complex macros, both the real and imaginary components are signaling NaNs. If imaginary types are not supported, then NANSIF, NANSI, and NANSIL are not defined.

The operation _Imaginary_I*y, where y is of real type domain and is a signaling NaN, should not trigger the signaling NaN (as this is a change of type domain and a data movement, not a multiplication operation). The operation x+y, where x is a real type and y is an imaginary type, and one or both of x and y are signaling NaNs, should not trigger the signaling NaN(s) (as this is a change of type domain and a data movement, not an addition operation). Operations such as real-imaginary, imaginary*_Imaginary_I, complex*_Imaginary_I, imaginary/_Imaginary_I and complex/_Imaginary_I need not trigger signaling NaNs; they should be consistent with other operations that just change the sign of a value.

7.12 Mathematics <math.h>

The macros

       NANSF
       NANS
       NANSL

are defined if and only if the implementation supports signaling NaNs for the float, double, and long double types, respectively. If defined, they each expand to a constant expression, of their respective type, representing a signaling NaN. They shall be usable in initialization.

The current version of this specification does NOT add a new number classification macro, but, instead adds a issignaling() function-like macro. This allows existing code that uses fpclassify() and checks for FP_NAN to retain its meaning; it means the argument was some kind of NaN.

The second version of this specification had a new number classification macro

        FP_NANS

which expands to an integer constant expression with value distinct from all the other number classification macros. FP_NANS is used to classify Signaling NaNs, e.g., fpclassify(signaling NaN) shall return FP_NANS. FP_NAN is used to classify Quiet NaNs, e.g.,fpclassify(quiet NaN) shall return FP_NAN. Together, FP_NANS and FP_NAN should cover all NaNs. The advantages of this approach are: it is closest to the spirit of IEEE-754 class() function (this might not be true after IEEE-754 is revised in 200x); some existing implementations are already doing this (they may spell the FP_NANS macro differently); and C99 implied(?) (paragraph 6 of 7.12 Mathematics <math.h>: FP_* implementation defined extension) this is how support for signaling NaNs should be done; the new function issignaling() is not required. The disadvantage of this approach is it changes meaning of existing code (fpclassify() being FP_NAN was true for all NaNs, now is true only for quiet NaNs).

The first version of this specification had a FP_NANQ, that was distinct from both FP_NAN and FP_NANS, with FP_NAN = the set { FP_NANQ union FP_NANS }. But, some problems were discovered with that. fpclassify(signaling NaN) cannot return both FP_NAN and FP_NANS. fpclassify(quiet NaN) cannot return both FP_NAN and FP_NANQ. So, if an implementation has FP_NANQ (not required by this specification), then FP_NANQ shall equal FP_NAN.

Results for the inquiry macros specified in the remainder of this section are undefined if the argument is not of floating type. These macros should not trigger signaling NaNs.

isnan() is true (nonzero) for all NaNs, both quiet and signaling.

For, isfinite(), isinf(), isnan(), and isnormal(), both quiet and signaling NaNs should produce the same behaviour, with the only possible difference being signaling NaNs might trigger.

7.12.1 Treatment of error conditions

Add to paragraph 2 (the one about domain errors): A signaling NaN argument shall be treated as an invalid argument and the function should return a quiet NaN, unless stated otherwise.

7.12.3.x The issignaling macro

Synopsis

        #include <math.h>
        int issignaling(real-floating x);

Description

The issignaling macro determines whether its argument value is a signaling NaN. First, an argument represented in a format wider than its semantic type is converted to its semantic type (which should trigger it). Then determination is based on the type of the argument.

Returns

The issignaling macro returns a nonzero value if and only if its argument has a signaling NaN value.

There is no compelling reason to add a corresponding macro to test for a quiet NaN, along the lines of isnanq() or isquiet(). Without it, a test for a quiet NaN would be done as: isnan(x) && !issignaling(x).

7.12.11.x The nans functions

Synopsis

        #include <math.h>
        double nans(const char *tagp);
        float nansf(const char *tagp);
        long double nansl(const char *tagp);

Description

An implementation declares a nans function if it supports signaling NaNs in the type of the function. The call nans("n-char-sequence") is equivalent to strtod("NAN(SIGn-char-sequence)", (char**) NULL); the call nans("") is equivalent to strtod("NAN(SIG)", (char**) NULL). If tagp does not point to an n-char sequence or an empty string, the call is equivalent to strtod("NAN(SIG)", (char**) NULL). Calls to nansf and nansl are equivalent to the corresponding calls to strtof and strtold.

Returns

The nans functions return a signaling NaN with content indicated through tagp, provided they don't trigger it first.

Some thought was given to changing the meaning of the existing nan() function by enhancing it so that nan("SIG"), nan("NANS"), or nan("SNAN") would return a signaling NaN. We decided it was better to add new functions so as to make it explicit that the user wants a signaling NaN.

Quiet change: No matter how we spell a signaling NaN data for I/O, either, NANS(...), NAN(SIG...), NAN(NANS...), or NAN(SNAN...), external data files that contain them might lead to subtle silent problems when used on systems that do not provide support for signaling NaNs.

7.19.6.1 The fprintf function

For a signaling NaN value, the implementation has the options to trigger the signaling NaN or to convert it in one of the styles [-]nan(sig) or [-]nans(sign-char-sequence) -- which style, and the interpretation of any n-char sequence, is implementation-defined.

Use of an upper case format specifier, E, F, or G, results in INF, INFINITY, NAN, or NAN(SIG) instead of inf, infinity, nan, or nan(sig).

7.19.6.2 The fscanf function

All valid syntax -- including infinity, NaN, and signed zero -- is treated in the same manner as strtod.

The fscanf function should not trigger a signaling NaN that it produces in response to a signaling NaN input string.

By not triggering signaling NaNs, fscanf provides a way of setting signaling NaN values. This might appear to be in conflict with the IEEE floating-point standards which require that binary-decimal conversion trigger signaling NaN input; however, the conversion of NAN(SIG) input need not be regarded as decimal-to-binary conversion.

7.20.1.3 The strtod, strtof, and strtold functions

The expected form of the subject sequence (after an optional sign) is augmented to include:

-- NAN(SIG) or NAN(SIGn-char-sequence), ignoring case in the NAN and SIG parts.

Strings of the form NAN(SIG) or NAN(SIGn-char-sequence) produce signaling NaNs, if supported, else are treated as quiet NaNs. If a signaling NaN is produced, the implementation has the option of returning it or triggering it. An implementation may use the n-char-sequence to determine extra information to be represented in the NaN's significand; which n-char sequences are meaningful is implementation-defined.

The option to trigger a signaling NaN is needed by implementations whose function-return mechanism involves conversion between different formats, which may trigger the signaling NaN. For example, an implementation that returns functions' results in a wider-than-float register might not be able to return a signaling NaN result for strtof.

For quiet NaNs, NAN was chosen over NANQ for brevity and because the predominance of NaNs in I/O are expected to be quiet.

Some thought was given to using "NANS(n-char-sequences)" or "SNAN(n-char-sequences)" as the means to encode signaling NaNs. IEEE standard 854 says that the string representation of NaNs, both quiet and signaling, should start with "NAN"; so of these two, NANS is better than SNAN. But, it was decided it is better to use NAN(SIG...) as that does not break existing implementations; the use of SIG as part of the n-char-sequence is coming from implementation defined forms, while both NANS() and SNAN() would be new and not portable to existing implementations that do not support this extension.

7.24.2.1 The fwprintf function

This function is extended in the same manner as fprintf.

7.24.2.2 The fwscanf function

This function is extended in the same manner as fscanf.

7.24.4.1.1 The wcstod, wcstof, and wcstold functions

These functions are extended in the same manner as strtod, strtof, and strtold.

F.8.2 Expression transformations

Any transformations that removes binary arithmetic operators are invalid (as that would convert code that always triggers a signaling NaN into code that might not trigger the signaling NaN).

F.9 Mathematics <math.h>

Change paragraph 11 to: Functions with a quiet NaN argument return a quiet NaN result and raise no floating-point exception, except where stated otherwise. Functions with a signaling NaN argument raise the "invalid" floating-point exception, convert the signaling NaN to a quiet NaN, and process that quiet NaN, except where stated otherwise. Note: As part of the function call, a signaling NaN argument may be triggered, so the function might never see the signaling NaN. To make a signaling NaN aware math library function, one might need to add:

    x *= 1.0f; /* trigger any signaling NaN */

as well add _WANT_SNAN to turn off certain optimizations.