FP256
256-bit binary float. One word, 71 digits at any magnitude, every op rounded once.
FP127 has 38 digits between 2^−128 and 2^127 and nothing outside; the edges show where that ends. FP256 fills the whole word, so the digits travel with the value.
The word
Sign in bit 255, a 19-bit exponent (bias 262,143), a 236-bit fraction with an implicit one: 237 significant bits, about 71 digits, from 10^−78,900 to 10^78,900. No NaN, no infinity, no subnormals, no negative zero. Zero is the all-zero word; any other word with exponent 0 reverts OutOfRange. Equality is bit equality.
[ s | E, 19 bits | F, 236 bits ] value = (-1)^s (1 + F / 2^236) 2^(E - 262143)
Rounding: once per op, ties away from zero. Overflow reverts; underflow rounds to zero. ONE is 0x3ffff << 236.
| digits | range | |
|---|---|---|
| FP127 | 38 | 2^−128 to 2^127 (±1.7 × 10^38) |
| ABDKMathQuad | 34 | 10^±4,932 |
| FP256 | 71 | 10^±78,900 |
Deployed form
- network
- sepolia (chain id 11155111)
- address
- 0x2776d2BEE09683F95016eaF512939a7E21751C27
- deployer
- 0x4e59b44847b379578588920cA78FbF26c0B4956C (the CREATE2 proxy)
- salt
- "FP256 v2" = 0x4650323536207632000000000000000000000000000000000000000000000000
- init code hash
- 0x1b10bbdfc3aa5723cfddb97d4883f491ec13a454d53a73316e363c5038e7c558
- runtime
- 10272 bytes, keccak 0xeca52ecb6c09ca4083f88969b0a9ad381c82a867f9bcdf81f5c74a797d582740
- compiler
- Yul, solc 0.8.24+commit.e11b9ed9, optimizer 200 runs, shanghai
- deployed
- block 11928043, source commit 3c99510
- verified
- Sourcify, Blockscout. Etherscan has no Yul path.
- record
- FP256.md, fp256.json
Inline form
forge install securedataresearch/blockulator
echo "fp256/=lib/blockulator/contracts/src/fp256/" >> remappings.txt
import {FP256Lib} from "fp256/FP256Lib.sol";
// a per-second rate over a year, with the rate itself held to 71 digits
int256 r = FP256Lib.div(FP256Lib.fromFixed18(0.05e18), FP256Lib.fromUint(31536000));
int256 g = FP256Lib.pow(FP256Lib.add(FP256Lib.ONE, r), FP256Lib.fromUint(31536000));
int256 balanceWad = FP256Lib.toFixed18(FP256Lib.mul(FP256Lib.fromFixed18(principalWad), g));
fromFP127 / toFP127 convert exactly whenever the value fits; fromFixed18 / toFixed18 convert to WAD.
Release two
Every op, exp, ln and pow included, returns the correctly rounded word at every committed vector. Release one borrowed FP127's kernels and held 146, 149 and 123 bits on those three. Release two computes at 2^−254 with 512-bit products: 2^f from a 16-entry table and a degree-26 polynomial, log2 from twelve reduction stages and a degree-17 polynomial, each within 2^−250 before the one rounding.
The ops
| op | what | domain | reverts | bits min / mean | gas object | gas inline |
|---|---|---|---|---|---|---|
| add(int256,int256) 0xa5f3c23b |
a + b, rounded once (half up on the magnitude). The result is exact whenever it is representable; a - a is +0. | any two numbers | Overflow() when the sum does not fit; OutOfRange() for a word that is not a number | exact | 2137 | 1440 |
| sub(int256,int256) 0xadefc37b |
a - b, rounded once. Exact whenever representable (Sterbenz: always for b/2 <= a <= 2b). | any two numbers | Overflow() when the difference does not fit; OutOfRange() for a word that is not a number | exact | 2221 | 1506 |
| mul(int256,int256) 0xbbe93d91 |
a * b: the 474-bit product of the significands, rounded once. | any two numbers | Overflow() when the product does not fit; OutOfRange() for a word that is not a number | exact | 2157 | 1366 |
| div(int256,int256) 0x43509138 |
a / b: floor(ma 2^255 / mb) by two exact 2^128 stages, rounded once. | b != 0 | DivisionByZero() for b == 0; Overflow() when the quotient does not fit; OutOfRange() for a word that is not a number | exact | 2512 | 2115 |
| sqrt(int256) 0x665df460 |
sqrt(x): the significand shifted to an even exponent, its integer square root at 2^136 (Newton: a 256-bit seed, one 2^128 stage, one 2^256 stage, corrected by squaring), rounded once. Never overflows or underflows. | x >= 0 | OutOfRange() for x < 0 or a word that is not a number | exact | 3164 | 2794 |
| exp(int256) 0xe46751e3 |
e^x = 2^(x log2 e): the 512-bit product of the significand and log2 e in two words (2^-508), split into the integer part and a fraction at 2^-254, then the 2^f kernel and one rounding: correctly rounded at every committed vector. Below 2^-118 the series 1 + x (1 + x/2) at full precision; below 2^-237 exactly one; zero below about -181,700. | x < 2^18 | Overflow() when e^x does not fit (x above about 181,705); OutOfRange() for a word that is not a number | exact | 6608 | 7145 |
| ln(int256) 0x8e6f2353 |
ln(x) = (E - bias + log2(M / 2^236)) ln 2: the exponent exactly, the mantissa through the log2 kernel at 2^-254, the sum scaled to fill a word, one product with ln 2 and one rounding: correctly rounded at every committed vector. Within 2^-8 of one, d = x - 1 is exact and ln(1 + d) = d * series(d) at 2^-254, so the relative precision holds all the way down. | x > 0 | OutOfRange() for x <= 0 or a word that is not a number | exact | 6345 | 6882 |
| pow(int256,int256) 0x92b0c5b2 |
x^y = 2^(y log2 x): log2 x as a scaled word from the kernel (or the near-one series), its exact 512-bit product with y's significand split into the integer part and a fraction at 2^-254, then the 2^f kernel and one rounding. The kernel's 2^-250 on log2 x bounds the result at about 250 - log2|t| bits for t = y log2 x. 0^0 = 1. Shortcuts for y in {1, 2, 1/2, -1}. | x >= 0 | OutOfRange() for x < 0; DivisionByZero() for 0^y with y < 0; Overflow() when the result does not fit | exact | 9648 | 10916 |
| neg(int256) 0x54a53dc1 |
-x: the sign bit flipped; -0 is 0. | any number | OutOfRange() for a word that is not a number | exact | 851 | 339 |
| abs(int256) 0x1b5ac4b5 |
|x|: the sign bit cleared. | any number | OutOfRange() for a word that is not a number | exact | 761 | 338 |
| sign(int256) 0xae610b48 |
-1, 0 or 1 as numbers. | any number | OutOfRange() for a word that is not a number | exact | 1121 | 384 |
| cmp(int256,int256) 0xc688366a |
-1, 0 or 1 as plain integers: the order of a and b. | any two numbers | OutOfRange() for a word that is not a number | exact | 1385 | 691 |
| lt(int256,int256) 0x30880038 |
1 if a < b, else 0, as a plain integer. | any two numbers | OutOfRange() for a word that is not a number | exact | 1023 | 695 |
| eq(int256,int256) 0xaa2c45bf |
1 if a == b, else 0. Representations are unique, so this is bit equality. | any two numbers | OutOfRange() for a word that is not a number | exact | 1196 | 577 |
| fromUint(uint256) 0xe3814b19 |
The number x, exact below 2^237, rounded once above. | any x | never | exact | 1632 | 993 |
| fromFP127(int256) 0x736c53c3 |
The number x / 2^128: exact whenever |x| has at most 237 significant bits (every |x| below 2^237, and larger ones with trailing zeros), rounded once otherwise. | any 127.128 word | never | exact | 1312 | 926 |
| toFP127(int256) 0x7c90b7f9 |
The 127.128 word nearest to x, half up on the magnitude: zero below 2^-129, one raw unit from there to 2^-128. | |x| < 2^127 | Overflow() at or above 2^127 (-2^127 is allowed); OutOfRange() for a word that is not a number | exact | 1123 | 643 |
| fromFixed18(int256) 0x9aea0a13 |
The number x / 10^18: the integer as a number (exact below 2^237), divided by 10^18 with one rounding. | any 18-decimal word | never | exact | 2699 | 2822 |
| toFixed18(int256) 0xa2745c74 |
The 18-decimal word nearest to x, half up on the magnitude, from the exact 512-bit product M 10^18. | |x| 10^18 < 2^255 | Overflow() when x 10^18 does not fit int256; OutOfRange() for a word that is not a number | exact | 1334 | 821 |
Bits are relative correct bits on the decoded values against exact rationals and mpmath at 300 decimal places over the committed vectors; "exact" is bit-identical. Gas is the median over each op's input ladder: "object" includes the staticcall to the deployed FP256, "inline" is FP256Lib. Sources: ops.json, precision.json, gas.json.
Source: contracts/src/fp256/FP256.yul.src, built by FP127's generator; object and library proven equal on all of int256. Full reference: contracts/src/fp256/README.md.