The Secret Life of NaN

The floating point standard defines NaN (Not-a-Number) to represent non-numeric values, but its 51-bit payload offers a clever hack for storing type information in dynamic languages.
The floating point standard defines a special value called Not-a-Number (NaN) which is used to represent, well, values that aren’t numbers. Double precision NaNs come with a payload of 51 bits which can be used for whatever you want– one especially fun hack is using the payload to represent all other non-floating point values and their types at runtime in dynamically typed languages.
the non-secret life of NaN
When I say “NaN” and also “floating point”, I specifically mean the representations defined in IEEE 754-2008, the ubiquitous floating point standard. This standard was born in 1985 (with much drama!) out of a need for a canonical representation which would allow code to be portable by quelling the anarchy induced by the menagerie of inconsistent floating point representations used by different processors.
Floating point values are a discrete logarithmic-ish approximation to real numbers. Since the NaN I’m talking about doesn’t exist outside of IEEE 754-2008, let’s briefly take a look at the spec.
An extremely brief overview of IEEE 754-2008
The standard defines these logarithmic-ish distributions of values with base-2 and base-10. For base-2, the standard defines representations for bit-widths for all powers of two between 16 bits wide and 256 bits wide. These are the only standardized bitwidths, meaning, if a processor supports 32 bit floating point values, then it’s highly likely it will support it in the standard compliant representation.
Let’s look at binary16, the base-2 16 bit wide format:
1 sign bit | 5 exponent bits | 10 mantissa bits
S E E E E E M M M M M M M M M M
Briefly, though, here are some examples: the take-away is you can use these 16 bits to encode a variety of values.
0 01111 0000000000 = 1
0 00000 0000000000 = +0
1 00000 0000000000 = -0
More interestingly, though, the standard also defines some special values: ±infinity, and “quiet” & “signaling” NaN.
What IEEE 754-2008 says about NaNs
All binary NaN bit strings have all the bits of the biased exponent field E set to 1. A quiet NaN bit string should be encoded with the first bit (d1) of the trailing significand field T being 1. A signaling NaN bit string should be encoded with the first bit of the trailing significand field being 0.
For example, in the binary16 format, NaNs are specified by the bit patterns:
s 11111 1xxxxxxxxxx = quiet (qNaN)
s 11111 0xxxxxxxxxx = signaling (sNaN)
Notice that this is a large collection of bit patterns! Even ignoring the sign bit, there are 2^(number mantissa bits - 1) bit patterns which all encoded a NaN! We’ll refer to these leftover bits as the payload. Modern commodity hardware commonly uses 64 bit floats; the double-precision format has 52 bits for the mantissa, which means there are 51 bits available for the payload.
Signaling NaNs afford representations for uninitialized variables and arithmetic-like enhancements. Quiet NaNs should afford retrospective diagnostic information inherited from invalid or unavailable data and results. To facilitate propagation of diagnostic information contained in NaNs, as much of that information as possible should be preserved in NaN results of operations.
This means the official suggestion in the floating point standard is to leave a qNaN exactly as you found it, in case someone is using it propagate “diagnostic information” using that payload we saw above.
What can we do with the payload?
People have used the NaN payload to pass around data & type information in dynamically typed languages, including implementations in Lua and Javascript. Because if your language is dynamically typed, then the type of a variable can change at runtime, which means you absolutely must also pass around some type information.
Source: Hacker News














