blockulator

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.

digitsrange
FP127382^−128 to 2^127 (±1.7 × 10^38)
ABDKMathQuad3410^±4,932
FP2567110^±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

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