Every glowing dot is a real single-precision float generated with the exact hardware formula:
Linear scale = true number line → almost all values sit extremely close to zero (the bright bar).
Log scale = position ∝ log₁₀(value) → shows that floats exist at every order of magnitude from ~10⁻³⁸ up to 1.
WebGPU: instance buffer holds every float + phase. Vertex shader maps value → screen X (linear/log) + bobbing. Fragment shader makes soft circles. Additive blending shows density.
In scientific notation a number is written as a significand (also called the mantissa) multiplied by a power of ten (or two in binary).
Value scaling: The significand carries the precision (the significant digits), while the exponent controls the magnitude (how large or small the number is). Together they let computers represent both very large and very tiny numbers with a fixed number of bits.
In IEEE 754 binary floating-point the significand is stored in normalized form (usually with a leading 1 that is implicit).
That is exactly why the formula you see in the visualization is written as (1 + mantissa) × 2exponent.
Cyan / blue points (left side) = very small floating-point numbers (close to 0).
Pink / magenta points (right side) = floating-point numbers that are relatively close to 1.0 (for example 0.5, 0.75, 0.9, 0.99, etc.).
In Linear scale you see very few pink points because almost all possible float32 values are packed tightly near zero.
In Log scale the pink points become more visible because the scale spreads out the larger magnitudes.
The dense cluster of values near zero that you see on the Linear scale is not an accident — it was a deliberate design choice made by the IEEE 754 committee (led by William Kahan) in the late 1970s / early 1980s.
| Approach | Spacing of numbers | Dynamic range | Result |
|---|---|---|---|
| Fixed-point | Uniform | Very limited | Not practical for scientific computing |
| Floating-point (IEEE 754) | Denser near zero | Extremely wide | What we use today |
The designers prioritized relative accuracy and a huge dynamic range over uniform spacing.
Yes — there is a fixed, deterministic number of values that IEEE 754 float32 can represent.
| Category | Count | Notes |
|---|---|---|
| Total possible bit patterns | 4,294,967,296 (2³²) | Every possible 32-bit combination |
| Distinct finite real numbers | 4,278,190,079 | Counting +0 and –0 as the same value |
| Finite bit patterns (incl. ±0) | 4,278,190,080 | Treating +0 and –0 as distinct |
| +Infinity and –Infinity | 2 | |
| NaN payloads | 16,777,214 | Many different NaN bit patterns |
Positive finite numbers (including +0): 2,139,095,040
Negative finite numbers (including –0): 2,139,095,040
Of the positive numbers:
• 1 zero
• 8,388,607 denormalized (subnormal) numbers
• 2,130,706,432 normal numbers (254 exponents × 2²³ significands)
In the closed interval [0, 1] there are exactly 1,056,964,610 distinct non-negative float32 values (this is what the visualization is sampling from).
When the 8 exponent bits of a float32 are all set to 1 and the 23-bit fraction is not zero, the value is a NaN (Not a Number). This leaves 23 bits that can hold arbitrary data — the NaN payload.
There are \(2^{23} - 1 = 8{,}388{,}607\) possible payloads for positive NaNs and the same number for negative NaNs, giving a total of 16,777,214 different NaN bit patterns.
One of the lead architects of IEEE 754, William Kahan, deliberately left these bits available so that software (or hardware) could record why or where a NaN was created. The idea was “retrospective diagnosis”: a long calculation could finish, and then you could inspect the payload to see what went wrong.
The most brilliant and widely deployed use of NaN payloads is NaN-boxing (also called NaN-tagging).
In dynamically typed languages, every value normally needs both its data and a type tag. NaN-boxing solves this elegantly:
Because modern CPUs only use the lower 48 bits of a 64-bit pointer, the 51–52 available payload bits in a double are more than enough to store a pointer plus type information. The result: every value in the language fits in a single 64-bit machine register.
Major systems that use NaN-boxing:
This technique is one of the reasons modern JavaScript engines can be surprisingly fast despite being dynamically typed.
NA (missing value) so it can be distinguished from ordinary calculation NaNs.While the original diagnostic vision of Kahan was only partially realized in mainstream software, the creative reuse of NaN space for NaN-boxing turned out to be far more impactful than anyone expected in the 1980s.
1. CPU & GPU Hardware
Modern processors contain dedicated Floating-Point Units (FPUs). The silicon is designed to implement the exact IEEE 754 rules (bit layout, rounding, NaN, Infinity, denormals). Instruction sets such as x86 SSE/AVX, ARM NEON, and RISC-V all have special floating-point instructions that follow the standard.
2. Compilers
Compilers must respect IEEE 754 when generating code. They cannot freely rearrange floating-point operations the way they can with integers. Flags like -ffast-math deliberately break strict compliance for speed.
3. Software
Almost all numerical software depends on it: games, graphics, machine learning, scientific simulations, finance, physics engines, etc.
Here are real situations where understanding the non-uniform density and other IEEE 754 behaviors prevents bugs or performance problems:
Example 1 – Random numbers in [0, 1)
Because of the density near zero, a naïve 32-bit integer divided by 2³² produces a biased distribution.
Example 2 – Comparing floating-point numbers
Two calculations that should be equal often differ by a few units in the last place (ULPs).
Example 3 – Denormal performance trap
Denormalized numbers (the extra values very close to zero) are handled in hardware much more slowly on most CPUs.
Example 4 – Catastrophic cancellation
Subtracting two nearly equal numbers destroys significant digits. This is why many numerical algorithms are carefully rewritten to avoid it.
Understanding the density near zero, the limited number of significand bits, and special values (NaN, Infinity, denormals) helps you write more robust and faster numerical code.