signexponentfractionthe four IEEE 754 binary formats, drawn to scale

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.

sign · 1 bitexponent · 8 bitsfraction · 23 bits

The number above is 0.1, the value the inspector below opens on. Change it there and this changes with it.

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.

parameterbinary32what it is
w8exponent field width, in bits
t23fraction field width, in bits
p24precision, in bits: t plus the implied leading 1
emax127largest exponent of a finite value
emin-126smallest exponent of a normal value, 1 − emax
bias127added to the exponent before it is stored, so the field is unsigned
encoding4 bytes8 hex digits; little-endian in memory on every mainstream machine
InspectorAny number, to every bit and every digit

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.

SpacingHow far apart neighbouring values are, at every magnitude

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.

wherehow it is spellednative?
C, C++, Java, C#, Go, Rust, Swiftfloat · float · float · float · float32 · f32 · Floatyes
JavaScriptFloat32Array, Math.fround()storage only: there is no binary32 scalar, and Math.fround rounds a Number to the nearest binary32
Pythonnp.float32, array('f'), struct 'f'through NumPy and the buffer modules; the language's own float is binary64
Fortranreal(kind=real32) · REAL*4 · realyes, and the default real
GLSL, HLSL, Metal, WGSL, CUDA, OpenCLfloatthe native type of every GPU, at full rate
x86-64, ARM64, RISC-VSSE / NEON / F extensionhardware, 8 or 16 lanes per vector instruction, fused multiply-add included
microcontrollersfloatan 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 tileCFT_FP328 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.

PlaygroundOne operation, all five attributes
attributeresult, 9 digitsencodingflags

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.

toolbest forthe catch
the compiler's contraction switch: -ffp-contract=off (GCC, Clang), /fp:strict (MSVC)one rounding per operation, as written, so two compilers agreeGCC 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:fastspeed, when the last bits do not matter and you have proved itreassociation, 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 simdeight or sixteen lanes per instructiona vectorised reduction sums in a different order from the scalar loop, so the answer changes with the vector width
GLSL, HLSL, WGSL, CUDA, Metalthe GPU's full ratethe 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-MATHcorrectly rounded binary32 functions, faster than the binary64 onesseveral of its binary32 functions have been merged into glibc; elsewhere it is a library to link
Herbierewriting an expression so it loses fewer of its seven digitsaccuracy, not reproducibility
binary64 for the accumulatorsumming binary32 data: keep the running total in a doublecosts nothing on a CPU and halves the throughput on a GPU
libcft (cft-fp256), software backenda 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 Arduinoa 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.

Conformance replayThe published binary32 vectors, replayed in this tab
Not run yet.

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.

This site's own experimentThe exactness census: Collatz trajectories, and the ones binary32 cannot finish

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).