IEEE 754-2019 · binary interchange format · 32 bits
binary32
The format of the GPU
A 32-bit IEEE 754 number: one sign bit, eight bits of exponent, twenty-three bits of fraction, and the arithmetic every CPU and GPU made in the last forty years does at full rate.
Also called single precision, float, f32, REAL*4, np.float32, Float32Array.
Anatomy
What the 32 bits mean
A binary32 value is a sign, a biased exponent and a fraction, packed as s · e · f from the most significant bit down. When the exponent field is neither all zeros nor all ones the value is (−1)s × 1.f × 2e − 127: the leading 1 is implied, so 23 stored bits give 24 bits of precision. All zeros in the exponent means a subnormal, (−1)s × 0.f × 2-126, which lets the format lose precision gradually rather than dropping to zero. All ones means infinity when the fraction is zero and a NaN otherwise; the fraction's top bit says whether the NaN is quiet or signaling.
| parameter | binary32 | what it is |
|---|---|---|
| w | 8 | exponent field width, in bits |
| t | 23 | fraction field width, in bits |
| p | 24 | precision, in bits: t plus the implied leading 1 |
| emax | 127 | largest exponent of a finite value |
| emin | -126 | smallest exponent of a normal value, 1 − emax |
| bias | 127 | added to the exponent before it is stored, so the field is unsigned |
| encoding | 4 bytes | 8 hex digits; little-endian in memory on every mainstream machine |
Type a decimal (0.1, 1e10, -0, inf, nan), a hexadecimal float (0x1.8p3), or a raw encoding (0x followed by exactly 8 hex digits). Click a bit to flip it. The decimal-to-binary conversion is correctly rounded under the attribute you pick, and "inexact" tells you the decimal you typed is not a binary32 value, only nearest to one. The exact decimal expansion has no digit limit: the library's conversion is exact at every length, which is what makes this widget possible.
What it can hold
7.22 decimal digits, and every integer to 16,777,216
Precision is 24 bits, which is 7.22 decimal digits: any decimal with 7 significant digits survives a round trip through binary32, and 9 digits are enough to write any binary32 value down so that it reads back to the same bits. Every integer up to 224 = 16,777,216 is exact; above it the spacing between representable numbers is 2, then 4, then 8, and an integer that lands between them is rounded to a neighbour. The largest finite value is about 3.40e38, the smallest normal about 1.18e-38, and the subnormals reach down to 2-149, about 1.40e-45.
The spacing is what matters in practice. Between two consecutive powers of two the representable values are evenly spaced, 223 of them per binade, and the spacing doubles with every binade. Relative to the value it is always between 2−24 and 2−23; absolute, it depends entirely on how big the value is.
Where it lives
Everywhere, at full rate, for forty years
binary32 is the format hardware likes. Every CPU since the 1980s computes it natively, every GPU is built around it, every shader language makes it the default, and the vector units of a modern processor do eight or sixteen of them per instruction. It is the format of graphics, of audio, of physics in games, of sensor data on microcontrollers, and, until the narrower formats arrived, of machine learning. Its case is bandwidth: four bytes per number is half of binary64, and for anything that streams through memory faster than it computes, half the bytes is twice the speed.
It is also the format that looks like it has more precision than it does. Seven digits is enough for a screen coordinate and not enough for a bank balance, and the 224 integer cliff at 16,777,216 is low enough that ordinary counts walk off it.
| where | how it is spelled | native? |
|---|---|---|
| C, C++, Java, C#, Go, Rust, Swift | float · float · float · float · float32 · f32 · Float | yes |
| JavaScript | Float32Array, Math.fround() | storage only: there is no binary32 scalar, and Math.fround rounds a Number to the nearest binary32 |
| Python | np.float32, array('f'), struct 'f' | through NumPy and the buffer modules; the language's own float is binary64 |
| Fortran | real(kind=real32) · REAL*4 · real | yes, and the default real |
| GLSL, HLSL, Metal, WGSL, CUDA, OpenCL | float | the native type of every GPU, at full rate |
| x86-64, ARM64, RISC-V | SSE / NEON / F extension | hardware, 8 or 16 lanes per vector instruction, fused multiply-add included |
| microcontrollers | float | an FPU on most ARM Cortex-M4F and up; software on Cortex-M0 and the 8-bit parts, at a few thousand operations a second |
| the cft-fp256 tile | CFT_FP32 | 8 lanes per beat at 135 MHz, and it loses: one tile is 1.6x behind an x86-64 workstation's FPU and 7.2x behind an M2 Pro, which vectorises this format hard; four tiles pass the workstation and still lose to the laptop. binary32 is carried so that one contract covers the whole ladder (measured) |
Where it breaks
Seven digits go quickly
The failures of binary32 are the failures of binary64 arriving sooner. The integer cliff is at sixteen million instead of nine quadrillion; a millimetre is lost at ten kilometres instead of at the orbit of Neptune; a sum of a hundred thousand terms is wrong in the third digit instead of the twelfth. And one failure is its own: because a compiler is allowed to fuse a multiply and an add into one rounding, the same source line can give different bits with different compilers, and two GPU vendors can disagree in the last bit of the same shader and then diverge. Every case below runs here, and where your browser can do binary32 arithmetic itself, through Math.fround, its answer is compared bit for bit.
Every case above is computed in this tab by the same library that scores itself against the published vectors at the bottom of the page. Where your browser has the format natively, its own answer is shown beside the contract's, and the two are compared bit for bit.
Rounding and flags
Five ways to round, five things that can go wrong
Every arithmetic operation computes the exact result and then rounds it once, under one of five attributes: to nearest with ties to even (the default everywhere), toward zero, toward −∞, toward +∞, and to nearest with ties away from zero. The directed attributes are how interval arithmetic gets rigorous bounds; ties-to-away is what some decimal conventions expect. Alongside the result, five flags record what happened: inexact when rounding changed the value, underflow when a tiny result was also inexact, overflow when the exact result was too large, divideByZero for a finite divided by zero, and invalid for an operation with no meaningful answer, such as 0/0 or ∞ − ∞, which delivers a quiet NaN.
| attribute | result, 9 digits | encoding | flags |
|---|
The operands are parsed under to-nearest first, so what differs between the rows is only the operation's own rounding. A result that is the same in all five rows was exact.
Tools and libraries
What to reach for at binary32
At this format the tools are mostly switches: the arithmetic is in the hardware, and the question is whether the compiler and the driver are computing what the source says.
| tool | best for | the catch |
|---|---|---|
the compiler's contraction switch: -ffp-contract=off (GCC, Clang), /fp:strict (MSVC) | one rounding per operation, as written, so two compilers agree | GCC contracts by default at any optimisation level outside strict ISO mode; Clang's default has changed across versions. Check, do not assume |
-ffast-math, -Ofast, /fp:fast | speed, when the last bits do not matter and you have proved it | reassociation, no subnormals, no NaN checks, and it leaks into every library linked with it. The single most common way a binary32 result becomes unreproducible |
SIMD intrinsics and #pragma omp simd | eight or sixteen lanes per instruction | a vectorised reduction sums in a different order from the scalar loop, so the answer changes with the vector width |
| GLSL, HLSL, WGSL, CUDA, Metal | the GPU's full rate | the shading languages permit the driver to contract, reorder and use lower-precision sin; precise and invariant qualifiers are how a shader asks it not to. Three vendors, three answers is the default state |
| CORE-MATH | correctly rounded binary32 functions, faster than the binary64 ones | several of its binary32 functions have been merged into glibc; elsewhere it is a library to link |
| Herbie | rewriting an expression so it loses fewer of its seven digits | accuracy, not reproducibility |
| binary64 for the accumulator | summing binary32 data: keep the running total in a double | costs nothing on a CPU and halves the throughput on a GPU |
| libcft (cft-fp256), software backend | a definition of the correct binary32 answer for every operation, scored against the published vectors, in C with no dependencies, on anything from a browser tab to a 16 MHz Arduino | a CPU wins on throughput at this format by a wide margin, and the FPGA tile loses to a laptop. The row is here for the contract: the same bits on every one of those machines, which the hardware row above does not promise |
Same bits everywhere
The format where two vendors disagree, and the one that runs on an Arduino
binary32 is where cross-vendor divergence is famous. A shader compiled by three GPU drivers is three different sequences of roundings, because each driver may fuse, reorder and approximate differently, all within the rules. For a renderer that iterates, a geometry library, a physics step, the last bit at frame one is a different picture at frame three thousand. The proven case that cft-fp256 exists to serve is atlas-engine's deterministic geometry library, which pins every operation and produces one hash across NVIDIA, AMD and Intel GPUs. The contract this page runs is the same bar reached from the other side: one exact result per operation, defined once, scored everywhere.
"Everywhere" is the point at this format. The library has no floating-point dependence of its own, it is integer arithmetic over 32-bit limbs, so it computes the same bits on a machine with no FPU at all. The project's embedded record has it replaying the published binary32 and binary64 vector sets on an ESP32, 508,000 cases with no disagreement, and its CFT_TINY profile fits the binary32 and binary64 arithmetic into the 32 KB of flash and 2 KB of RAM of an ATmega328P: the same source, on an Arduino Uno, giving the bits this tab gives. The replay below is that check, run here.
The sample is the one the library's own conformance page embeds: every 59th line of each published set for this format, plus the first line of any opcode the stride missed, so every opcode class is present. It runs through cft_conformance(), the same C code path every backend of the library is judged by. The full 1,068,915-case sets replay on the conformance page, which accepts the generated files by drag and drop.
Up and down the ladder
One click right when seven digits run out
The move to binary64 is the cheapest upgrade in numerical computing: on a CPU it costs nothing but memory, and it moves the integer cliff from sixteen million to nine quadrillion and the digits from seven to sixteen. Do it when a count can pass 224, when positions are measured far from their origin, when a sum runs long, and whenever a binary32 result is going to be fed back into the next step of anything. Keep binary32 for the data itself when the data was never more than seven digits to begin with: an image, a waveform, a model's weights.
The exactness census below is the cleanest picture of the cliff: exact integer arithmetic, and the fraction of it that binary32 can no longer do exactly, watched as the numbers climb.
Every Collatz step is exact integer arithmetic while the numbers fit in 24 bits: halving an even number costs nothing, and 3n + 1 is one fused multiply-add that either fits or rounds. A second fused multiply-add computes the residual y − 3n and tells, per trajectory, whether the first one rounded. A trajectory whose tripling rounds has left exactness and is counted as escaped, keeping the last value it provably held. This is the binary32 run of cft-fp256's Collatz workload, whose sweep over the first 100,000 starting values escapes 87 times, the first at 26,623.
The number in the inspector travels with you: a neighbouring site opens on the same value, widened exactly or rounded once to its own format, and says so.
Reading
- IEEE Std 754-2019, IEEE Standard for Floating-Point Arithmetic. The definition; clause 3 has the formats, clause 4 the rounding attributes, clause 5 the operations, clause 9 the recommended functions.
- David Goldberg, What Every Computer Scientist Should Know About Floating-Point Arithmetic, ACM Computing Surveys, 1991. Still the best first read.
- Jean-Michel Muller et al., Handbook of Floating-Point Arithmetic, 2nd ed., Birkhäuser, 2018. The reference for how the operations are actually built.
- Nicholas J. Higham, Accuracy and Stability of Numerical Algorithms, 2nd ed., SIAM, 2002. What the rounding errors do once they are in an algorithm.
- William Kahan's notes, in particular How Futile are Mindless Assessments of Roundoff in Floating-Point Computation? (2006).