The C programming language adopts the IEEE 754 floating-point arithmetic. This includes the representation of non-finite floating-point values: on the one hand, there are positive and negative infinity. On the other hand, there is NaN (Not a Number), signifying invalid arithmetical operations.
This post discusses the NaN values in more detail.
Arithmetic and comparisons in C
To understand the purpose of the NaN floating-point values, we first study their arithmetic behavior. In particular, we see a few examples where NaN is created.
NaN values appear as the result of operations that do not have a valid value, neither a finite value nor an infinity. Some examples include:
±0 / ±0 = NaN∞ + (−∞) = NaN±∞ / ±∞ = NaNsqrt( -1.0 ) = NaN
Any ordinary arithmetic operation in C will yield a NaN result if any of the operands is NaN. That way, once a NaN appears in the computations, it will propagate throughout the computations.
C comparison operators adhere to the following rules if a NaN appears:
Any ordered comparison (
<,<=,>,>=) involving NaN is false.Equality (
==) with NaN is false, even NaN == NaN is false.Inequality (
!=) with NaN is true.
In that sense, NaN is the only value that is not equal to itself. The preferred way of detecting NaN in a C code is the function isnan(), included from <math.h>.
Quiet and signaling NaNs
NaNs have two subtypes according to IEEE:
Quiet NaN (qNaN): Propagates through most arithmetic without triggering an exception. Used to represent missing or undefined results (e.g.,
0/0,sqrt(−1)).Signaling NaN (sNaN): Intended to raise an invalid-operation exception on first use. Used for uninitialized data or data explicitly marked invalid. In most C environments, hardware exceptions are masked by default, so sNaNs are often immediately converted to qNaNs on use.
If exceptions are unmasked, then sNaNs trigger an invalid-operation exception before becoming qNaNs.
Bit representations
In binary IEEE 754 formats, a floating-point number has three fields:
Sign bit: 0 for positive, 1 for negative.
Exponent field: All bits set to 1 (maximum exponent value) marks infinities and NaNs.
Fraction (mantissa) field: Distinguishes between infinities and NaNs.
NaNs are implemented as follows:
Exponent: all ones
Fraction: nonzero
The distinction between quiet and signaling NaN is done via the leading bit of the fraction part:
qNaN: leading mantissa bit = 1
sNaN: leading mantissa bit = 0
The remaining bits of the fractional part, and the sign bit, generally have no further meaning. However, they may carry additional diagnostic information, the specifics of which are implementation-defined.
Example in single precision:
qNaN example: 0 11111111 10000000000000000000000 (0x7FC00000)
sNaN example: 0 11111111 01000000000000000000000 (0x7FA00000)
Purposes and applications
NaN encodes undefined results in floating-point computations. Upon an invalid operation, a NaN is introduced and propagates through subsequent arithmetic operations. This concept is deemed preferable to just stopping the computations since it allows to catch the data at a later point in time.
The difference between signaling and quiet NaNs is mostly historical.
sNaNs are intended to cause an invalid operation exception on first use, so the program can catch the event immediately. This is useful in debugging numerical code, detecting uninitialized values, or halting on unexpected invalid input before further contamination of results.
qNaNs propagate silently through computations, carrying payload bits until a result is examined. This avoids interrupting long computations where invalid intermediate values are tolerated or expected.
The distinction is less visible in modern C user code. In practical C on general-purpose OSes, the NaN subtype distinction mostly survives as an unused bit pattern convention. Practically,
On most mainstream platforms (x86, ARM) the default floating-point exception mask prevents trapping, so sNaNs automatically convert to qNaNs before most user code can react.
Libraries and languages that require uninterrupted computation almost always generate qNaNs directly.
The sNaN mechanism is still in hardware for standards compliance and for specialized domains (high-reliability, safety-critical systems, debugging numerical kernels), but typical applications never see them unless explicitly constructed and tested with exceptions unmasked.
Top comments (0)