blockulator

Float

FP256: 256-bit binary floating point for the EVM. One word, 71 digits at any magnitude, no walls; built on FP127's kernels.

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

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