FP127 is a fixed point: 38 digits between 2^−128 and 2^127, and nothing outside. The edges measured where that ends. FP256 is the second ruler: a binary float that fills the whole word, so the digits travel with the value.
The word
One int256 holding the bit pattern of a float: the sign in bit 255, a 19-bit biased exponent in bits 254..236 (bias 262,143), and a 236-bit fraction with an implicit leading one, so 237 significant bits, about 71 decimal digits, from 2^−262142 to just under 2^262145 (about 10^±78,900). Zero is the all-zero word and the only word with exponent 0; any other word with exponent 0 is not a number and every op reverts OutOfRange on it. There is no NaN, no infinity, no subnormal and no negative zero: every valid word is a finite number with one representation, equality is bit equality, and positive words order as unsigned integers.
[ s | E, 19 bits | F, 236 bits ] value = (-1)^s (1 + F / 2^236) 2^(E - 262143)
Rounding is half up on the magnitude (ties away from zero), once per op. Overflow reverts; underflow rounds to zero. ONE is 0x3ffff << 236.
Against the other two rulers on this site: FP127 holds 128 bits of fraction, so where the money lives it has 38 digits to FP256's 71 and to ABDKMathQuad's 34; past 2^±128 it has none, and the floats keep theirs. Quad uses 128 bits of the word; FP256 uses all 256.
Deployed form
- network
- sepolia (chain id 11155111)
- address
- 0x628b8dfC82954983a9E8E22056ffc767B4a26077
- deployer
- 0x4e59b44847b379578588920cA78FbF26c0B4956C (the CREATE2 proxy)
- salt
- "FP256 v1" = 0x4650323536207631000000000000000000000000000000000000000000000000
- init code hash
- 0xdb79b4b16f216a803757f7c89520212b48ce54eb5cf54a431465890f6ac80e42
- runtime
- 8037 bytes, keccak 0xcf5c047227240c591f5d213aeef3bfd90343707c06d9a13d3ca857c579de83a1
- compiler
- Yul, solc 0.8.24+commit.e11b9ed9, optimizer 200 runs, shanghai
- deployed
- block 11922179, source commit df5155d
- 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 and toFP127 bridge to FP127 (exact in both directions whenever the value fits), fromFixed18 and toFixed18 to WAD.
Release one
The arithmetic, the root, the comparisons and the conversions are exact to the 237-bit grid at every committed vector. exp, ln and pow reuse FP127's kernels, which compute at 2^−192 with polynomials fitted for a 128-bit result: exp holds 146 bits or better, ln 149, pow 123, with series bands near zero and near one so small arguments keep their relative precision. Release two is a kernel at 2^−250 with 512-bit products, for 236-bit transcendentals.
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 | 2119 | 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 | 2202 | 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 | 2133 | 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 | 2499 | 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 | 3137 | 2794 |
| exp(int256) 0xe46751e3 |
e^x = 2^(x log2 e), the product at 2^-192 and 2^f from FP127's kernel: about 145 correct bits. 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 | 146 / 200 | 5747 | 6159 |
| ln(int256) 0x8e6f2353 |
ln(x) = (E - bias) ln 2 + ln(M / 2^236): the exponent exactly, the mantissa through FP127's log2 kernel at 2^-192, one rounding. The kernel's polynomial is good to about 2^-153 absolute, so ln carries about 150 correct bits in general and 190 where the exponent dominates (powers of two are exact to 2^-192). Within 2^-24 of one, d = x - 1 is exact and ln(1 + d) = d * series(d), so the relative precision holds all the way down. | x > 0 | OutOfRange() for x <= 0 or a word that is not a number | 149 / 191 | 4521 | 4878 |
| pow(int256,int256) 0x92b0c5b2 |
x^y = 2^(y log2 x), with log2 x as a number from the exponent and FP127's kernel, the product rounded once, then 2^t. 0^0 = 1. Shortcuts for y in {1, 2, 1/2, -1}. The float product means a huge y against an x near one is handled by the format. | x >= 0 | OutOfRange() for x < 0; DivisionByZero() for 0^y with y < 0; Overflow() when the result does not fit | 123 / 181 | 8493 | 9505 |
| 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 | 1608 | 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 | 1293 | 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 | 2660 | 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.
The source is one Yul file, contracts/src/fp256/FP256.yul.src, read by the same generator as FP127's; the helpers it shares with FP127 are byte-identical copies, and the object and the library are proven equal on the full int256 domain. The format and every op are documented in contracts/src/fp256/README.md.