IEEE 754-2019 · binary interchange format · 256 bits
binary256
The format no one ships
A 256-bit IEEE 754 number: one sign bit, nineteen bits of exponent, two hundred and thirty-six bits of fraction. Seventy-one decimal digits. The standard defines it by formula and names it as its own example; no language has a keyword for it and no commercial processor computes it.
Also called octuple precision, binary{k} with k = 256, no language keyword.
Anatomy
What the 256 bits mean
A binary256 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 − 262143: the leading 1 is implied, so 236 stored bits give 237 bits of precision. All zeros in the exponent means a subnormal, (−1)s × 0.f × 2-262142, 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 | binary256 | what it is |
|---|---|---|
| w | 19 | exponent field width, in bits |
| t | 236 | fraction field width, in bits |
| p | 237 | precision, in bits: t plus the implied leading 1 |
| emax | 262143 | largest exponent of a finite value |
| emin | -262142 | smallest exponent of a normal value, 1 − emax |
| bias | 262143 | added to the exponent before it is stored, so the field is unsigned |
| encoding | 32 bytes | 64 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 64 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 binary256 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
71.34 decimal digits, and every integer to 2^237 (about 2.21e71)
Precision is 237 bits, which is 71.34 decimal digits: any decimal with 71 significant digits survives a round trip through binary256, and 73 digits are enough to write any binary256 value down so that it reads back to the same bits. Every integer up to 2237 = 2^237 (about 2.21e71) 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 1.61e78913, the smallest normal about 2.48e-78913, and the subnormals reach down to 2-262378, about 2.25e-78984.
The spacing is what matters in practice. Between two consecutive powers of two the representable values are evenly spaced, 2236 of them per binade, and the spacing doubles with every binade. Relative to the value it is always between 2−237 and 2−236; absolute, it depends entirely on how big the value is.
Where it lives
In no processor you can buy, and in one you cannot
IEEE 754 defines binary256 without naming it a basic format: clause 3.6 gives the formulas for any binaryk with k a multiple of 32 above 128, and then uses this one as its worked example, "binary256 would have p = 237 and emax = 262143". No language has a keyword for it. No commercial processor computes it. The libraries that reach it are multiprecision libraries set to 237 bits, which is close to the format and, as the cases below show, not the same thing.
The one hardware implementation is the cft-fp256 tile, an FPGA design for the AMD Alveo U50 whose 256-bit lane computes one binary256 element per beat with a 237-bit significand, or splits into two binary128, four binary64 or eight binary32. It reproduces the published vectors on silicon to the same bits as the software backend that runs this page. In software, that library runs on anything with a C compiler, on this tab, on a Raspberry Pi Pico and on an ESP32.
| where | how it is spelled | native? |
|---|---|---|
| C, C++, Fortran, Rust, Go, Java, Python, JavaScript | nothing | no type, no keyword, no literal syntax |
| MPFR and GMP | mpfr_set_prec(x, 237) | software; a 237-bit significand with MPFR's own exponent range, and the manual's recipe to narrow it to a binary format's |
mpmath, Julia's BigFloat, Boost's cpp_bin_float | mp.prec = 237 · setprecision(BigFloat, 237) · cpp_bin_float<71> | software, multiprecision, with the same distinction from a 754 format: no subnormals, no signaling NaNs, no interchange encoding unless you build one |
| double-double, quad-double | qd_real | about 212 bits from four binary64 values at hardware speed; not binary256, a binary64 exponent range, and no IEEE edges |
| the cft-fp256 tile | CFT_FP256 | hardware: 1 element per beat, 107 million elements a second on one tile, 427 million on four at about 35 watts; 5.5x over MPFR on one tile and 21.1x on four, against the best software on the same machine. Below about a thousand elements a library call still wins, and over PCIe rather than from device memory one tile is at parity with MPFR (measured) |
| libcft, the software backend | CFT_FP256 in C99 | 1.7 million elements a second on a laptop core, a few hundred cases a second on an ESP32, and this tab. Slower than MPFR by 6 to 19 times: it is the definition, not the racer |
Where it breaks
Seventy-one digits, and the one thing they cannot do
binary256 has a failure mode unlike the narrower formats': there is no second implementation to disagree with. At binary64 two libms differ in the last bit; at binary256 there is one library and a handful of multiprecision packages that approximate the format without being it. MPFR at 237 bits has a different exponent range, no subnormals unless you emulate them, no signaling NaNs and no payloads, so an MPFR result and a binary256 result are the same number only when nothing near an edge happened. The other failure is the honest one: for most computations, seventy-one digits are sixty more than the method deserves, and a fifteenth-order integrator at binary256 produces the same digits it produced at binary128. The cases below show the edges, which is where the format is a format and not a precision setting.
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, 73 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 binary256
Ranked by what the work is. At this width the question is rarely "which library is fastest" and usually "does this library mean the same thing as the format".
| tool | best for | the catch |
|---|---|---|
| MPFR and GMP | the fastest software arithmetic at 237 bits, correctly rounded, from any language with a binding | a precision is not a format: set emin and emax to binary256's, call mpfr_subnormalize after every operation, and accept that NaN payloads, signaling NaNs and the interchange encoding are yours to build |
mpmath, Julia's BigFloat | interactive work, checking, prototyping | MPFR's semantics at Python's or Julia's convenience; the same caveats |
| Arb, now part of FLINT | rigorous results: every value carries an error ball, so the answer is a proof | a different arithmetic, ball rather than point; the right tool when "rigorous" is the requirement and the wrong one when bit-exactness with a 754 format is |
| quad-double | about 212 bits at hardware speed, when the work is dense linear algebra and the exponent range of binary64 is enough | not binary256; no subnormals, no 754 edges, and a precision that is not quite the format's |
Boost.Multiprecision cpp_bin_float | C++ with operators, header-only | its own format, binary but not 754: a wider exponent, no subnormals, different rounding at the edges |
| libcft (cft-fp256), software or the tile | the one implementation of binary256 as a format: p = 237, emax = 262143, subnormals, the two zeros, quiet and signaling NaNs, all five rounding attributes, the five flags, the interchange encoding, every clause-5 operation and all 39 transcendentals correctly rounded; identical bits on this tab, a CPU, a microcontroller and the FPGA card, which is the only hardware that computes it | in software, 6 to 19 times slower than MPFR on the basic operations, because it is the reference and not the racer. On the card, the win needs resident execution: staging operands over PCIe puts one tile at about parity with MPFR |
Same bits everywhere
At this width the contract is the definition
At binary32 and binary64, "same bits everywhere" is a claim about agreement between implementations that already exist. At binary256 there are no others, so the claim has to be made differently: publish the definition, make it checkable by anyone, and hold the one implementation to it with the one independent arbiter that reaches the format. cft-fp256's definition is an exact, dependency-free Python model of every operation, from which 1,068,915 conformance cases are generated deterministically and published; the C library, the WebAssembly build running in this tab, the microcontroller build and the FPGA tile are all scored against those cases and never against each other. GNU MPFR, at 237 bits with the format's range emulated, arbitrates 739,234 transcendental cases and 999,000 division and square-root cases across the four formats with zero value and zero flag mismatches.
The replay below runs this format's sample of those cases through the reference implementation itself, here. The one program in nine languages prints 0x0eea048c14040a4e over this format's vectors on every platform in the project's compatibility record, and the card reproduced every published case on silicon through one tile and through four.
What the width is for is measured rather than argued, in the project's five workloads, each written for a property the contract has and a conventional library does not. A deep-zoom Mandelbrot reference orbit at a magnification where one pixel is 3.1e-61 wide: binary256 holds the centre to zero pixels of error, and a binary64 reference is 9.4e30 pixels off, wrong from iteration 1, with every flag clear (ZOOM.md). Exact integer arithmetic below 2237, with the inexact flag as a certificate: Collatz trajectories and Lucas-Lehmer on 59-bit limbs through 219937 − 1 (MERSENNE.md). Rigorous enclosures, where 14 of 15 ill-conditioned binary64 enclosures straddle zero and 0 of 15 at binary256 (ENCLOSE.md). A symplectic orbit whose angular-momentum drift is 9.0e-69 against binary64's 1.4e-13 on the same steps, 2184 apart, while the energy drift, which is the method's, is identical at every format (ORBITS.md). All five run in a browser on the demos page, each checksum-matched to its C tool.
A Mandelbrot frame 128 pixels wide at a magnification where one pixel is 1.6e-61 across: the frame measured above, at 128 pixels across for ZOOM.md's 64, so a pixel here is half as wide and the same distance is twice as many of them. The reference orbit z ← z² + c at the frame's centre is iterated a thousand times in the format named, and every pixel is then computed from it by perturbation in binary64, exactly as cft-fp256's cft-zoom tool does. Left, the reference at binary256. Right, the same frame with the reference at binary64: a different picture, because a binary64 centre is 1.9e31 pixels from the true one and its orbit is wrong from iteration 1, with every flag clear. That frame is flat, and it is meant to be: the binary64 number nearest this centre is −2 exactly, the tip of the set, and the orbit of −2 is −2, 2, 2, 2, …, every step exact, which is how a wrong orbit raises no flag. The frame that orbit gives is the neighbourhood of −2, where there is no minibrot to find, in two shades that meet on a vertical seam. The seam is the line where c crosses −2: to its left no part of the set exists, and the pixels escape sooner. Each run's records are hashed into a chain and compared with the chain the C tool produced for exactly this configuration, so the frame you see is the tool's frame to the bit.
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
Down to binary128, stated first
The measured case against this format comes before the case for it. cft-rebound's horizon study ran REBOUND's IAS15 integrator at binary64, binary128 and binary256 on the same problems and steps, and at every step size a user would choose, binary256 produced the same digits as binary128: a fifteenth-order method's truncation error, which scales as dt15, sits far above the binary128 floor until the steps are absurdly small. The one regime the sweep found where the width mattered was the outer solar system at ten-to-twenty-day steps, worth one to six orders. Everywhere else, the cost was real, ten to twenty corrector passes a step against four to nine, and the benefit was zero. If the method's error is above the binary128 floor, binary128 is the answer and it is one click left.
binary256 earns its place where the arithmetic itself is the object: a chaotic map iterated thousands of times from a point that has to be exactly the point, an integer that has to stay an integer to 2237, an enclosure that has to be narrow enough to exclude zero, a libm at a narrower format that has to be checked to more than twice its own precision. Those are the workloads above, and they are the reason a tile exists for this rung and not, say, for binary512.
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).