IEEE 754 NaN carries up to 51 spare payload bits; dynamically typed runtimes exploit this to pack type tags and non-float values into a single 64-bit word.
Key Takeaways
Every double-precision NaN has 51 bits of usable payload; the standard explicitly encourages preserving it for “diagnostic information” propagation.
Quiet NaN propagates silently; signaling NaN fires an exception flag, making it a natural sentinel for uninitialized variables.
NaN-boxing encodes all JS values in 64 bits: pointers in bits 0-47, integers in FFFF:0000:IIII:IIII, doubles shifted by 2^48 to avoid the 0x0000/0xFFFF ranges.
JavaScriptCore (Safari/WebKit), SpiderMonkey (nun-boxing/pun-boxing), and LuaJIT (NaN-tagging) all ship production NaN-boxing; the JSC source comment is the clearest public documentation.
The scheme depends on x86-64 using only 48-bit virtual addresses; any future 64-bit full-address CPUs would break every NaN-boxing VM.
Hacker News Comment Review
Commenters agree NaN-boxing’s main runtime benefit is avoiding heap allocation for floats, which directly reduces GC pressure – a concrete engineering win, not just a curiosity.
The 48-bit pointer assumption surfaces as the key fragility: if hardware ever uses all 64 address bits, NaN-boxing VMs break silently without architectural mitigation.
There is light discussion on using signaling NaN to detect uninitialized variables at the language level, with the D language cited as a shipped example of float-defaults-to-NaN semantics.
Notable Comments
@WalterBright: D initializes floats to NaN by default, not 0.0 – catches uninitialized-variable bugs that 0.0 silently masks.
@GMoromisato: uses NaN-boxing in GridWhale; frames it as “Infinite Hotel” – you can always add a type tag, and float allocation savings at GC time are the practical payoff.