BL19 is a float, so its digits move with the value: about 71 at any size, where BL127 keeps 38 at 1 and 8 at 10−30. Every op is correctly rounded: none of 360,590 random inputs was misrounded (sweep).
The format
A value is one int256: the sign in bit 255, a 19-bit exponent (bias 262,143) and a 236-bit fraction with an implicit leading one. That is 237 significant bits, about 71 digits, from 10−78,913 to 1078,913.
value = (-1)^s (1 + F / 2^236) 2^(E - 262143), with s the sign, E the exponent and F the fraction.
There is no NaN, infinity, subnormal or negative zero. Zero is all zero bits, any other value with exponent 0 reverts with OutOfRange, and two values are equal exactly when their bits are. Each op rounds once, ties away from zero. Overflow reverts with Overflow, underflow rounds to zero, and division by zero reverts with DivisionByZero. ONE is 0x3ffff << 236.
| digits | range | add | mul | div | exp | ln | pow | sqrt | |
|---|---|---|---|---|---|---|---|---|---|
| BL19 | 71 | 10±78,913 | 662 | 530 | 800 | 3,278 | 3,106 | 5,964 | 1,205 |
| BL127 | 38 at 1 | 2−128 to 2127 (±1.7 × 1038) | 128 | 255 | 412 | 2,824 | 1,934 | 3,410 | 297 |
| ABDKMathQuad | 34 | 10±4,932 | 734 | 709 | 818 | 5,668 | 16,356 | 23,479 | 760 |
Inline library, median gas per call on the same inputs (full table). BL19 carries twice binary128's digits and costs less than Quad on every op here but sqrt. BL127 costs less than BL19 on every op here, with fewer digits.
The deployed contracts
- network
- sepolia (chain id 11155111)
- address
- 0x94d9d43D6202827429a8058959eeCf6d232b8729
- verified
- Sourcify, Blockscout. Etherscan has no Yul path.
Deployment record
- deployer
- 0x4e59b44847b379578588920cA78FbF26c0B4956C (the CREATE2 proxy)
- salt
- "FP19 v6" = 0x4650313920763600000000000000000000000000000000000000000000000000
- init code hash
- 0x92b09dc2068287399b37c8b1d9c1f962b51b15bfd613afbcb1ef5ac09494a9af
- runtime
- 17221 bytes, keccak 0xfebbf6fbbd3eee6a4501f2b7ddef98a522d7f39fc9f32345f0012d3a72641814
- compiler
- Yul, solc 0.8.38+commit.1835d247, optimizer 200 runs, osaka
- deployed
- block 12015306, source commit 81e4771
- record
- BL19.md, bl19.json
BL19 v6 (Blockulator 3.0) is five contracts from one source. The record above is the BL19 contract's; BL19.md has all five.
| contract | what it holds | address | ops |
|---|---|---|---|
| BL19 | the number | 0x94d9d43D6202827429a8058959eeCf6d232b8729 | add sub mul div inv sqrt cbrt hypot gavg; neg abs sign; cmp lt eq min max clamp; floor ceil round frac; avg dist zeroFloorSub lerp; factorial; mulDown mulUp divDown divUp; every conversion |
| BL19E | the elementary functions, on one exp/log kernel | 0x8e5eE786bDD7c35a2867Fd329441d0BD0fB61092 | exp exp2 exp10 expm1 ln log2 log10 log2Up log1p pow powu compound |
| BL19H | the hyperbolic functions | 0xF059611C1c42543DE6EE82353Eb082Fd8999081f | sinh cosh tanh atanh |
| BL19N | the normal distribution | 0xCe9F09D646B37a9100F05ED1FFD14E9f43180214 | erf erfc normCdf |
| BL19S | Lambert W | 0xf74ba90535224870830e6e89D2b328d4cc09DE48 | lambertW0 lambertWm1 |
BL19H has its own copy of the exp/log kernel because BL19E with the hyperbolics was over the 24,576-byte limit, and BL19N holds the erf family's fitted polynomial. factorial is a table in the core, for n ≤ 170. pi is the constant BL19Lib.PI, and gcd was removed in v5. The inline library holds all 65 ops.
The contracts need the CLZ opcode (EIP-7939, evmVersion osaka), so they do not run on zkSync Era, Avalanche C-Chain or Polygon zkEVM (the list). BL19 v5 (Blockulator 2.2: BL19, BL19E, BL19H and BL19S), v4 (Blockulator 2.1: BL19, BL19X, BL19X2 and BL19X3) and v3 (Blockulator 2.0) stay live at the addresses in the record.
Install
The import path is the same however you install it:
# Foundry, from git
forge install securedataresearch/blockulator
echo "@blockulator/contracts/=lib/blockulator/contracts/src/" >> remappings.txt
# npm, yarn or pnpm (Hardhat needs no remapping; Foundry reading node_modules needs this one)
npm install @blockulator/contracts
echo "@blockulator/contracts/=node_modules/@blockulator/contracts/" >> remappings.txt
# Soldeer
forge soldeer install blockulator~<version>
echo "@blockulator/contracts/=dependencies/blockulator-<version>/" >> remappings.txt
Compile BL19Lib.sol with via_ir = true in foundry.toml. Through the IR, add drops from 677 to 545 gas and exp from 3,277 to 2,848 (profile.md).
import {BL19Lib} from "@blockulator/contracts/bl19/BL19Lib.sol";
// a per-second rate over a year, with the rate itself held to 71 digits
int256 r = BL19Lib.div(BL19Lib.fromFixed18(0.05e18), BL19Lib.fromUint(31536000));
int256 g = BL19Lib.compound(r, 31536000); // (1 + r)^n without ever rounding 1 + r
int256 balanceWad = BL19Lib.toFixed18(BL19Lib.mul(BL19Lib.fromFixed18(principalWad), g));
fromFixed18 and toFixed18 convert to and from WAD. fromBL127 is exact below 2109 and rounds once above; toBL127 rounds to the nearest 2−128.
Typed API
BL19Type.sol is opt-in: type BL19 is int256, with arithmetic and comparison operators bound to the library and every other op attached (x.exp(), a.mulUp(b)). bl19FromWad, bl19FromInt and bl19FromUint convert in, toWadDown, toUintUp and the rest convert out, toBL127 and toBL19 cross to BL127's type, and ONE and PI are typed constants. The wrapper only re-types, so results and reverts are BL19Lib's, and through the IR the gas is the same.
import {BL19, ONE, bl19FromWad, bl19FromInt} from "bl19/BL19Type.sol";
import {BL127} from "bl127/BL127Type.sol";
BL19 b = bl19FromWad(1.5e18) * ONE.exp(); // 1.5e; * is BL19Lib.mul
bool between = b > bl19FromInt(4) && b < bl19FromInt(5);
uint256 charged = b.toUintUp(); // 5, ceiled
BL127 n = b.toBL127(); // the nearest BL127 value
Integrating
Two rules, each from the replay of a real exploit (integrating has the replays and code).
- Store the state in the format. Keep a running balance, index or accumulator as an BL19 value, and round to token units only on the way out. In Bunni v2's replay this keeps a balance the attack drained from 28 wei to 4 at 35.6 wei, as exact arithmetic does.
- Round toward the protocol at the token edge. Round down what you pay out and round up what you take in:
toUintDownandtoUintUp,toFixed18DownandtoFixed18Up,mulDownandmulUp,divDownanddivUp. Under this rule Resupply's borrow against 1 wei of collateral reverts.
The ops
Each op's worst-case precision against its gas. Hover a dot for the op.
| op | what | domain | reverts | bits (min/mean) | gas, object | gas, inline | bytes 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, OutOfRangeOverflow() when the sum does not fit; OutOfRange() for a word that is not a number |
CR | 1,126 | 662 | 632 |
| sub(int256,int256) 0xadefc37b |
a - b, rounded once. Exact whenever representable (Sterbenz: always for b/2 <= a <= 2b). | any two numbers | Overflow, OutOfRangeOverflow() when the difference does not fit; OutOfRange() for a word that is not a number |
CR | 1,190 | 745 | 633 |
| mul(int256,int256)proven 0xbbe93d91 |
a * b: the 474-bit product of the significands, rounded once. | any two numbers | Overflow, OutOfRangeOverflow() when the product does not fit; OutOfRange() for a word that is not a number |
CR | 1,063 | 530 | 337 |
| div(int256,int256)proven 0x43509138 |
a / b: floor(ma 2^255 / mb) by one exact 512-by-256 division, rounded once. | b != 0 | DivisionByZero, Overflow, OutOfRangeDivisionByZero() for b == 0; Overflow() when the quotient does not fit; OutOfRange() for a word that is not a number |
CR | 1,370 | 800 | 444 |
| sqrt(int256) 0x665df460 |
sqrt(x)sqrt(x): the significand shifted to an even exponent, its integer square root at 2^136 (Newton: the word root of the top 256 bits, one residual step in a word, corrected by squaring), rounded once. Never overflows or underflows. |
x >= 0 | OutOfRangeOutOfRange() for x < 0 or a word that is not a number |
CR | 1,685 | 1,205 | 531 |
| exp(int256) 0xe46751e3 |
e^x = 2^(x log2 e)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 (within 6.1 units of 2^-254) and one rounding when that bound clears the rounding boundary, else e^r - 1 in two words for r = x - k ln 2: correctly rounded. Below 2^-118, 1 + x + x^2/2 floored exactly on the 2^-254 grid (the cubic only breaks ties) and rounded once; a positive x whose lowest bit is 2^-118, below 2^-84, makes that sum an exact tie the kernel cannot see past, so it takes the same path. Exactly one below 2^-238; zero below about -181,700. |
x < 2^18 | Overflow, OutOfRangeOverflow() when e^x does not fit (x above about 181,705); OutOfRange() for a word that is not a number |
CR | 3,317 | 3,278 | 6,232 |
| ln(int256) 0x8e6f2353 |
ln(x) = (E - bias + log2(M / 2^236)) ln 2ln(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 when its 5 units clear the rounding boundary, else u ln 2 + ln(mantissa) in two words (one Newton step on the kernel's value): correctly rounded. Within 2^-8 of one, d = x - 1 is exact and ln(1 + d) = d * (ln 2/2)(1 - y) P(y^2), y = d/(2 + d), at 2^-254 (in two words when needed), so the relative precision holds all the way down. |
x > 0 | OutOfRangeOutOfRange() for x <= 0 or a word that is not a number |
CR | 3,283 | 3,106 | 6,532 |
| pow(int256,int256) 0x92b0c5b2 |
x^y = 2^(y log2 x)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; one rounding when the error bound (which grows with |y| or |t|) clears the rounding boundary, else the middle stage: log2 x within 2^-272 (mid254.py), t exact from it and the same 2^f kernel, its bound 10 units whatever |y|; else an exact tie when x^y has at most 238 bits, else log2 x in two words (wln) times y and 2^f by wexpm1: correctly rounded. 0^0 = 1. Shortcuts for y in {1, 2, 1/2, -1}. |
x >= 0 | OutOfRange, DivisionByZero, OverflowOutOfRange() for x < 0; DivisionByZero() for 0^y with y < 0; Overflow() when the result does not fit |
CR | 5,994 | 5,964 | 14,797 |
| log1p(int256) 0x8552a5b0 |
ln(1 + x) without forming a rounded 1 + xln(1 + x) without forming a rounded 1 + x. Below 2^-4, x times ln(1 + x)/x = (ln 2/2)(1 - y) P(y^2), y = x/(2 + x), at 2^-254 (P the kernel's minimax polynomial below 2^-8, a wider one above), so the relative precision holds all the way down; above, 1 + x exactly on a 2^-254 grid under its leading bit, the log2 kernel and one product with ln 2. Each fast value carries its error bound; when it reaches the rounding boundary the series or ln(1 + x) is recomputed in two words. Correctly rounded. |
x > -1 | OutOfRangeOutOfRange() for x <= -1 or a word that is not a number |
CR | 2,846 | 2,587 | 6,329 |
| expm1(int256) 0x973fe005 |
e^x - 1 without cancellatione^x - 1 without cancellation. Below 2^-8, x times (e^x - 1)/x from its minimax polynomial at 2^-254; above, exp's split of x log2 e and its unrounded 2^f, with the one subtracted on the same 2^-254 grid; each within its bound, recomputed in two words when that reaches the rounding boundary. Correctly rounded; -1 below about -176. |
x < 2^18 | Overflow, OutOfRangeOverflow() when e^x - 1 does not fit (x above about 181,705); OutOfRange() for a word that is not a number |
CR | 2,785 | 2,395 | 7,802 |
| erf(int256) 0x7a959e36 |
The error functionThe error function. Below 1/2, x (2/sqrt pi) e^(-x^2) times the all-positive series sum (2x^2)^k / (2k+1)!!; above, 1 - erfc with erfc = e^(-x^2) g(x) from g's two minimax pieces or the asymptotic series (see erfc). e^(-x^2) comes from the exact square. One rounding when the fast value's error bound clears the rounding boundary, else the series in two words below 7 and 1 - erfc by the continued fraction in two words above: correctly rounded. +-1 from |x| >= 13. |
any number | OutOfRangeOutOfRange() for a word that is not a number |
CR | 9,676 | 10,624 | 17,031 |
| erfc(int256) 0xa79456da |
The complementary error function as a product, never a difference, for x >= 1/2The complementary error function as a product, never a difference, for x >= 1/2: erfc(x) = e^(-x^2) g(x), g = e^(x^2) erfc from two degree-66 minimax pieces on [1/2, 18) and the asymptotic series above, e^(-x^2) from the exact 474-bit square, so the tail keeps 71 digits until it underflows 2^-262142 near x = 427. 1 - erf below 1/2, 2 - erfc(-x) below zero. One rounding, or the wide second stage (the series and 1 - erf below 7, the continued fraction above, in two words) when the fast value's error bound reaches the rounding boundary: correctly rounded. |
any number | OutOfRangeOutOfRange() for a word that is not a number |
CR | 9,759 | 10,654 | 17,106 |
| normCdf(int256) 0x7b9745a0 |
The standard normal distribution function Phi(x) = erfc(-x / sqrt 2) / 2, with e…The standard normal distribution function Phi(x) = erfc(-x / sqrt 2) / 2, with e^(-x^2/2) from the exact square, so the left tail keeps 71 digits until it underflows near x = -603 (the 18-decimal grid is empty from about -8.3); one rounding, or erf's wide second stage at y = |x| / sqrt 2: correctly rounded; exactly one from x >= 13 sqrt 2. |
any number | OutOfRangeOutOfRange() for a word that is not a number |
CR | 11,442 | 12,354 | 17,159 |
| lambertW0(int256) 0x6de23d34 |
The principal branch of Lambert W, W(x) e^W(x) = x, W >= -1The principal branch of Lambert W, W(x) e^W(x) = x, W >= -1. Below 1 + e x = 1/8 (x < -0.322) the unknown is q = 1 + W on (q - 1) e^q + 1 = 1 + e x, with 1 + e x exact: the seed p S(p), p = sqrt(2 (1 + e x)), S a degree-16 fit (2^-44.8), and one sixth-order step with e^q - 1 from the wide exp kernel, whose error is 5 units of 2^-254 whatever q, so the branch point costs no precision; one rounding when that clears the rounding boundary. Elsewhere x (1 - x + 3x^2/2) or Winitzki's seed from a crude logarithm, fourth-order log-form steps at 2^-126 (one or two, with a table-driven logarithm) and one at word precision with a bound (no e^w, so the whole range up to W = 181,700 works), tested the same way. On a miss one Newton step at 2^-510 (the residual in three words) and one rounding: correctly rounded. W rounds to x below 2^-238. |
x >= -1/e | OutOfRangeOutOfRange() below -1/e or for a word that is not a number |
CR | 9,591 | 10,672 | 10,740 |
| lambertWm1(int256) 0xae94abc8 |
The secondary real branch of Lambert W, W(x) <= -1The secondary real branch of Lambert W, W(x) <= -1. Below 1 + e x = 1/8 (x < -0.322) as lambertW0 in q = 1 + W (negative here); above, the asymptotic seed L - ln(-L) + ln(-L)/L, L = ln(-x), from the crude logarithm, lambertW0's fourth-order log-form steps (one to three at 2^-126, one at word precision; no e^w: W reaches about -181,700 at the bottom of the format), its rounding test and, on a miss, its Newton step at 2^-510: correctly rounded. |
-1/e <= x < 0 | OutOfRangeOutOfRange() outside that or for a word that is not a number |
CR | 9,788 | 10,759 | 10,474 |
| powu(int256,int256) 0xb8846d69 |
x^n for |n| below 2^16 by binary exponentiation on a 256-bit significand with it…x^n for |n| below 2^16 by binary exponentiation on a 256-bit significand with its own exponent, each product keeping its top 256 bits, rounded once at the end: exact whenever x^n fits 256 bits (so every exactly representable power and every exact tie), else within (2n + 8) units, and through pow's middle stage when that reaches the rounding boundary. n < 0 inverts the final significand once. From 2^16 on, pow(|x|, n) with the sign restored. Correctly rounded for |n| < 2^237 (n is a number from there). 0^0 = 1. |
any number x, any plain integer n (not a number); the result must fit | Overflow, DivisionByZero, OutOfRangeOverflow() when x^n does not fit; DivisionByZero() for 0^n with n < 0; OutOfRange() for a word that is not a number |
CR | 6,694 | 6,647 | 15,557 |
| compound(int256,int256) 0xa31e7db8 |
(1 + rate)^n without forming a rounded 1 + rate (a per-second rate needs 266 bits for it)(1 + rate)^n without forming a rounded 1 + rate (a per-second rate needs 266 bits for it): log2(1 + rate) unrounded, as log1p takes it (the series below 2^-4, 1 + rate exact under its leading bit above), its exact product with n, then pow's 2^t; one rounding when its error bound clears the rounding boundary, else the middle stage as pow's (log2(1 + rate) within 2^-272, its bound 10 units whatever |n|), else an exact tie when the power has at most 238 bits, else log2(1 + rate) in two words times n exactly: correctly rounded. |
rate >= -1, any plain integer n (not a number); the result must fit | OutOfRange, DivisionByZero, OverflowOutOfRange() for rate < -1; DivisionByZero() for rate = -1 with n < 0; Overflow() when the result does not fit |
CR | 5,611 | 5,858 | 13,873 |
| neg(int256) 0x54a53dc1 |
-x: the sign bit flipped; -0 is 0. | any number | OutOfRangeOutOfRange() for a word that is not a number |
exact | 715 | 127 | 108 |
| abs(int256) 0x1b5ac4b5 |
|x|: the sign bit cleared. | any number | OutOfRangeOutOfRange() for a word that is not a number |
exact | 727 | 133 | 108 |
| sign(int256) 0xae610b48 |
-1, 0 or 1 as numbers. | any number | OutOfRangeOutOfRange() for a word that is not a number |
exact | 764 | 139 | 116 |
| cmp(int256,int256) 0xc688366a |
-1, 0 or 1 as plain integers: the order of a and b. | any two numbers | OutOfRangeOutOfRange() for a word that is not a number |
exact | 803 | 248 | 178 |
| lt(int256,int256) 0x30880038 |
1 if a < b, else 0, as a plain integer. | any two numbers | OutOfRangeOutOfRange() for a word that is not a number |
exact | 818 | 233 | 173 |
| eq(int256,int256) 0xaa2c45bf |
1 if a == b, else 0. Representations are unique, so this is bit equality. | any two numbers | OutOfRangeOutOfRange() for a word that is not a number |
exact | 830 | 176 | 129 |
| fromUint(uint256)proven 0xe3814b19 |
The number x, exact below 2^237, rounded once above. | any x | never | CR | 937 | 413 | 234 |
| fromBL127(int256) 0x61854220 |
The number x / 2^128The 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 | CR | 949 | 440 | 244 |
| toBL127(int256) 0x9c8b7775 |
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, OutOfRangeOverflow() at or above 2^127 (-2^127 is allowed); OutOfRange() for a word that is not a number |
CR | 914 | 343 | 313 |
| fromFixed18(int256)proven 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 | CR | 1,148 | 554 | 325 |
| 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, OutOfRangeOverflow() when x 10^18 does not fit int256; OutOfRange() for a word that is not a number |
CR | 1,030 | 480 | 351 |
| mulDown(int256,int256)proven 0x4b8d10e0 |
a * b rounded toward minus infinitya * b rounded toward minus infinity: the largest number at or below the exact product (the 474-bit product of the significands and a sticky bit). A product below the smallest number is zero when positive and minus the smallest number when negative. |
any two numbers | Overflow, OutOfRangeOverflow() when the product does not fit; OutOfRange() for a word that is not a number |
CR | 1,244 | 727 | 449 |
| mulUp(int256,int256)proven 0x1aabb2f7 |
a * b rounded toward plus infinitya * b rounded toward plus infinity: the smallest number at or above the exact product. A positive product below the smallest number is the smallest number, never zero. |
any two numbers | Overflow, OutOfRangeOverflow() when the product does not fit; OutOfRange() for a word that is not a number |
CR | 1,243 | 728 | 450 |
| divDown(int256,int256)proven 0xddb29e92 |
a / b rounded toward minus infinitya / b rounded toward minus infinity: div's exact floor quotient and a sticky bit from the remainder, mulmod(ma, 2^255, mb). |
b != 0 | DivisionByZero, Overflow, OutOfRangeDivisionByZero() for b == 0; Overflow() when the quotient does not fit; OutOfRange() for a word that is not a number |
CR | 1,464 | 987 | 544 |
| divUp(int256,int256)proven 0xe2bec1fe |
a / b rounded toward plus infinity: the smallest number at or above the exact quotient. | b != 0 | DivisionByZero, Overflow, OutOfRangeDivisionByZero() for b == 0; Overflow() when the quotient does not fit; OutOfRange() for a word that is not a number |
CR | 1,531 | 988 | 545 |
| toUintDown(int256)proven 0x5fe0506c |
floor(x) as a plain uint256, exact for every number in range: what a protocol pays out. | 0 <= x < 2^256 | OutOfRange, OverflowOutOfRange() for x < 0 or a word that is not a number; Overflow() at or above 2^256 |
exact | 871 | 405 | 284 |
| toUintUp(int256)proven 0x67d4ca8d |
ceil(x) as a plain uint256, exact: what a protocol charges. | 0 <= x <= 2^256 - 2^19 | OutOfRange, OverflowOutOfRange() for x < 0 or a word that is not a number; Overflow() at or above 2^256 |
exact | 937 | 406 | 285 |
| toIntDown(int256)proven 0x64f06660 |
floor(x) as a plain int256 (toward minus infinity), exact. | -2^255 <= x < 2^255 | Overflow, OutOfRangeOverflow() when floor(x) does not fit int256; OutOfRange() for a word that is not a number |
exact | 1,077 | 466 | 304 |
| toIntUp(int256)proven 0x5ed6bfec |
ceil(x) as a plain int256 (toward plus infinity), exact. | -2^255 <= x < 2^255 | Overflow, OutOfRangeOverflow() when ceil(x) does not fit int256; OutOfRange() for a word that is not a number |
exact | 1,124 | 467 | 305 |
| floor(int256)proven 0xda3ff648 |
The largest integral number at or below x, exact (x itself from 2^236 on, where every number is an integer). | any number | OutOfRangeOutOfRange() for a word that is not a number |
exact | 1,130 | 522 | 376 |
| ceil(int256)proven 0x42fba7ae |
The smallest integral number at or above x, exact. | any number | OutOfRangeOutOfRange() for a word that is not a number |
exact | 1,131 | 523 | 377 |
| toFixed18Down(int256) 0x32154abe |
The 18-decimal word at or below x (toward minus infinity), from the exact 512-bi…The 18-decimal word at or below x (toward minus infinity), from the exact 512-bit product M 10^18: what a protocol pays out in 18-decimal units. |
|x| 10^18 < 2^255 | Overflow, OutOfRangeOverflow() when the result does not fit int256; OutOfRange() for a word that is not a number |
CR | 1,309 | 723 | 447 |
| toFixed18Up(int256) 0x27066ea4 |
The 18-decimal word at or above x (toward plus infinity)The 18-decimal word at or above x (toward plus infinity): what a protocol charges; a positive x below 10^-18 is one unit, never zero. |
|x| 10^18 < 2^255 | Overflow, OutOfRangeOverflow() when the result does not fit int256; OutOfRange() for a word that is not a number |
CR | 1,335 | 724 | 448 |
| fromUintDown(uint256)proven 0xfebfc540 |
The largest number at or below x: x itself below 2^237, x truncated to 237 significant bits above. | any x | never | CR | 912 | 487 | 302 |
| fromUintUp(uint256)proven 0x35e13636 |
The smallest number at or above xThe smallest number at or above x: x itself below 2^237, x rounded up to 237 significant bits above (2^256 for the largest). |
any x | never | CR | 999 | 488 | 303 |
| min(int256,int256) 0x29aa9cbe |
The smaller of a and b: one of the two words, exact. | any two numbers | OutOfRangeOutOfRange() for a word that is not a number |
exact | 837 | 251 | 184 |
| max(int256,int256) 0x81fe5786 |
The larger of a and b: one of the two words, exact. | any two numbers | OutOfRangeOutOfRange() for a word that is not a number |
exact | 827 | 250 | 184 |
| clamp(int256,int256,int256) 0x7b8d0f0c |
min(hi, max(lo, x)), exact. When lo > hi the result is hi, as BL127's. | any three numbers | OutOfRangeOutOfRange() for a word that is not a number |
exact | 1,134 | 491 | 242 |
| avg(int256,int256) 0xebf274be |
(a + b) / 2 rounded once from the exact sum: never overflows, and below the format it rounds as every op does. | any two numbers | OutOfRangeOutOfRange() for a word that is not a number |
CR | 1,116 | 663 | 633 |
| dist(int256,int256) 0x2110b689 |
|a - b|, rounded once (sub's rounding is symmetric, so its magnitude is the rounded distance). | any two numbers | Overflow, OutOfRangeOverflow() when |a - b| does not fit; OutOfRange() for a word that is not a number |
CR | 1,230 | 819 | 660 |
| zeroFloorSub(int256,int256) 0xff78cf2d |
max(0, a - b), rounded once. | any two numbers | Overflow, OutOfRangeOverflow() when a - b does not fit; OutOfRange() for a word that is not a number |
CR | 1,294 | 829 | 663 |
| inv(int256)proven 0x3a02c983 |
1 / x: div's exact quotient floor(2^491 / m), rounded once. | x != 0 | DivisionByZero, Overflow, OutOfRangeDivisionByZero() for x == 0; Overflow() when 1/x does not fit; OutOfRange() for a word that is not a number |
CR | 1,249 | 747 | 401 |
| lerp(int256,int256,int256) 0x84e13b60 |
a + t (b - a), rounded once from the exact valuea + t (b - a), rounded once from the exact value: a, t b and -t a (computed once, in memory) summed exactly in a 1,024-bit window (terms far under it enter as their floor or their sign), so cancellation costs nothing and no intermediate can overflow. t is not clamped. |
any three numbers; the result must fit | Overflow, OutOfRangeOverflow() when the result does not fit; OutOfRange() for a word that is not a number |
CR | 6,890 | 6,515 | 2,741 |
| frac(int256) 0xae2785ac |
x - floor(x), rounded oncex - floor(x), rounded once: in [0, 1), and exact for x >= 0; a negative x within 2^-238 of an integer gives exactly 1, the nearest word to 1 - |x - ceil(x)|. |
any number | OutOfRangeOutOfRange() for a word that is not a number |
CR | 1,050 | 428 | 980 |
| round(int256)proven 0x58b4370b |
The nearest integral number, halves away from zero (BL19's tie rule; BL127's round takes halves up), exact. | any number | OutOfRangeOutOfRange() for a word that is not a number |
exact | 1,100 | 474 | 343 |
| log2Up(int256) 0xa1985bb6 |
ceil(log2 x) as a number: the exponent, plus one unless x is a power of two. Exact. | x > 0 | OutOfRangeOutOfRange() for x <= 0 or a word that is not a number |
exact | 1,169 | 679 | 381 |
| factorial(int256) 0x5ad78e8a |
floor(n)! rounded once, from a table of the 169 correctly rounded words for 2 <=…floor(n)! rounded once, from a table of the 169 correctly rounded words for 2 <= n <= 170 (exact to 64!), checked against the exact factorial for every n (make sweep). 0! = 1. |
0 <= n < 171 (170! is the largest factorial a binary64 holds) | OutOfRange, OverflowOutOfRange() for n < 0 or a word that is not a number; Overflow() from 171 |
CR | 1,217 | 719 | 8,211 |
| cbrt(int256) 0xd4b6188c |
The cube root, cbrt(-x) = -cbrt(x)The cube root, cbrt(-x) = -cbrt(x): floor(cbrt(N)) of the significand scaled to N = m 2^(477 + rr), 714 to 716 bits, by Newton with precision doubling (a seed from the top bits, four steps in one word to 85 bits, two residual steps to 170 and 239) and an exact three-word cube at the end, then rounded once. Exact for perfect cubes; never overflows. |
any number | OutOfRangeOutOfRange() for a word that is not a number |
CR | 3,196 | 2,734 | 960 |
| gavg(int256,int256) 0xa13ed853 |
The geometric mean sqrt(a b) of the exact 474-bit product, rounded once; never overflowsThe geometric mean sqrt(a b) of the exact 474-bit product, rounded once; never overflows. Two negatives give the positive root. |
a b >= 0 | OutOfRangeOutOfRange() if a and b have opposite signs (both nonzero) or for a word that is not a number |
CR | 2,569 | 2,099 | 713 |
| hypot(int256,int256) 0xc479210c |
sqrt(a^2 + b^2) rounded once from the exact sum of squares (floored only at a gr…sqrt(a^2 + b^2) rounded once from the exact sum of squares (floored only at a grid far under the root's), so ties of Pythagorean triples round away from zero; |a| when b is more than 2^120 smaller. No intermediate overflows. |
any two numbers; the result must fit | Overflow, OutOfRangeOverflow() when sqrt(a^2 + b^2) does not fit; OutOfRange() for a word that is not a number |
CR | 3,559 | 3,556 | 978 |
| exp2(int256) 0xdbbb06d2 |
2^x2^x: the integer part of x to the exponent, its fraction (exact at 2^-254 from 2^-18 up) through the 2^f kernel, one rounding when the bound clears the rounding boundary, else e^(f ln 2) by wexpm1 in two words: correctly rounded, exact for integral x. Exactly one below 2^-238; zero below -262,142. |
x < 262145 | Overflow, OutOfRangeOverflow() when 2^x does not fit (x at or above 262,145); OutOfRange() for a word that is not a number |
CR | 3,299 | 3,096 | 5,825 |
| exp10(int256) 0x4c0dafa2 |
10^x = 2^(x log2 10)10^x = 2^(x log2 10): the 512-bit product with log2 10 split as exp's, the 2^f kernel and one rounding when the bound clears the rounding boundary, else x log2 10 in three words and 2^f by wexpm1: correctly rounded, so 10^n is exact for n = 0 to 102. Exactly one below 2^-240. |
x below about 78,913.4 | Overflow, OutOfRangeOverflow() when 10^x does not fit; OutOfRange() for a word that is not a number |
CR | 3,349 | 3,177 | 5,846 |
| log2(int256) 0xe0d68737 |
log2(x) = (E - bias) + log2(M / 2^236)log2(x) = (E - bias) + log2(M / 2^236): the exponent exactly and the mantissa through the log2 kernel, one rounding when its 4 units clear the rounding boundary; within 2^-8 of one, d = x - 1 exact and d ln1pk(d) log2 e. Else the same in two words (pow's wide log2): correctly rounded, exact at powers of two. |
x > 0 | OutOfRangeOutOfRange() for x <= 0 or a word that is not a number |
CR | 3,262 | 2,885 | 6,741 |
| log10(int256) 0xb47ca3c7 |
log10(x) = log2(x) log10 2log10(x) = log2(x) log10 2: the exponent exactly, the mantissa through the log2 kernel, one product and one rounding when its 4 units clear the rounding boundary; within 2^-8 of one, ln's series times log10 e. Else u log10 2 + ln(mantissa) log10 e in three words: correctly rounded, exact at the powers of ten that are numbers (10^0 to 10^102). |
x > 0 | OutOfRangeOutOfRange() for x <= 0 or a word that is not a number |
CR | 3,426 | 3,101 | 6,742 |
| sinh(int256) 0x17cdc976 |
sinh(x) = (U + U/(1 + U)) / 2 with U = e^|x| - 1 from expm1's machinerysinh(x) = (U + U/(1 + U)) / 2 with U = e^|x| - 1 from expm1's machinery: below 2^-8, x times T = (e^|x| - 1)/|x| from its polynomial (no cancellation, so the relative precision holds all the way down); to 1/2, U from exp's unrounded kernel; above, (e^|x| - e^-|x|) / 2 from that kernel and its reciprocal. One rounding when the bound clears the rounding boundary, else the series or both exponentials in two words: correctly rounded. |
|x| below about 181,705 | Overflow, OutOfRangeOverflow() when sinh(x) does not fit; OutOfRange() for a word that is not a number |
CR | 3,640 | 3,670 | 8,007 |
| cosh(int256) 0xaa13e96a |
cosh(x) = (e^|x| + e^-|x|) / 2 from exp's unrounded kernel and its reciprocal, s…cosh(x) = (e^|x| + e^-|x|) / 2 from exp's unrounded kernel and its reciprocal, summed before the one rounding (no cancellation); else both exponentials in two words: correctly rounded. Exactly one below 2^-118. |
|x| below about 181,705 | Overflow, OutOfRangeOverflow() when cosh(x) does not fit; OutOfRange() for a word that is not a number |
CR | 3,674 | 3,692 | 6,322 |
| tanh(int256) 0x5e1f05c7 |
tanh(x) = U/(U + 2) with U = e^2|x| - 1 from expm1's machinerytanh(x) = U/(U + 2) with U = e^2|x| - 1 from expm1's machinery: below 2^-9, x times T/(1 + |x| T) with T = (e^2|x| - 1)/(2|x|) from its polynomial; to 1/2, U from exp's unrounded kernel; above, 1 - 2/(e^2|x| + 1) from that kernel. One rounding when the bound clears the rounding boundary, else sinh's and cosh's series or the exponential in two words: correctly rounded. Exactly +-1 once 2|x| log2 e passes 240. |
any number | OutOfRangeOutOfRange() for a word that is not a number |
CR | 3,740 | 3,717 | 8,082 |
| atanh(int256) 0xc6285a68 |
atanh(x) = log1p(y) / 2 for y = 2|x|/(1 - |x|)atanh(x) = log1p(y) / 2 for y = 2|x|/(1 - |x|): below 2^-6, x times R P with R = 1/(1 - |x|) and P = ln(1 + y)/y from log1p's series at y = 2|x| R (no cancellation, so the relative precision holds all the way down); above, (log2(1 + |x|) - log2(1 - |x|)) ln 2 / 2 with both arguments exact under their leading bits (the logarithms have opposite signs). One rounding when the bound clears the rounding boundary, else the odd series or the two logarithms in two words: correctly rounded. |
|x| < 1 | OutOfRangeOutOfRange() for |x| >= 1 or a word that is not a number |
CR | 4,016 | 3,882 | 6,061 |
Bits against exact rationals and mpmath at 300 decimal places over the committed vectors. CR: the correctly rounded word at every vector, to nearest or in the direction the name gives; exact: the result is always the exact value. proven: every input against a reference spec, on the proofs page. Open an op for its method; hover a reverts cell for when. Gas is the median over random inputs that take no shortcut for the 25 ops that have them (the transcendentals, roots and powers), else over the op's input ladder. "Object" includes the staticcall to the deployed object that holds the op (BL19, BL19E, BL19H, BL19N or BL19S); "inline" is BL19Lib. "Bytes inline" is the runtime code one op adds to a contract that calls it through BL19Lib, helpers included (default profile, legacy pipeline; hover for via_ir). Ops that share a kernel add less together than their rows (size.json's bundles). Inlining saves about 464 gas a call; staticcall the deployed object instead when inlining would take a contract near the 24,576-byte limit, or for a large op called rarely. Sources: ops.json, precision.json, gas.json, size.json.
Gas against BL127 and Quad
| op | BL19 deployed (staticcall) | BL19Lib (inline) | BL127Lib (inline), the same op | ABDKMathQuad |
|---|---|---|---|---|
| abs | 727 | 133 | 152 | 21 |
| add | 1,126 | 662 | 128 | 734 |
| atanh t | 4,016 | 3,882 | 2,941 | |
| avg | 1,116 | 663 | 114 | |
| cbrt t | 3,196 | 2,734 | 1,022 | |
| ceil | 1,131 | 523 | 147 | |
| clamp | 1,134 | 491 | 241 | |
| cmp | 803 | 248 | 476 | |
| compound t | 5,611 | 5,858 | 3,078 | |
| cosh t | 3,674 | 3,692 | 4,059 | |
| dist | 1,230 | 819 | 289 | |
| div | 1,370 | 800 | 412 | 818 |
| divDown | 1,464 | 987 | 475 | |
| divUp | 1,531 | 988 | 478 | |
| eq | 830 | 176 | 108 | |
| erf t | 9,676 | 10,624 | 8,427 | |
| erfc t | 9,759 | 10,654 | 7,586 | |
| exp t | 3,317 | 3,278 | 2,824 | 5,668 |
| exp10 t | 3,349 | 3,177 | 3,077 | |
| exp2 t | 3,299 | 3,096 | 2,697 | 5,031 |
| expm1 t | 2,785 | 2,395 | 2,960 | |
| factorial | 1,217 | 719 | 400 | |
| floor | 1,130 | 522 | 72 | |
| frac | 1,050 | 428 | 69 | |
| fromBL127 | 949 | 440 | 539 | |
| fromFixed18 | 1,148 | 554 | 140 | |
| fromUint | 937 | 413 | ||
| fromUintDown | 912 | 487 | 500 | |
| fromUintUp | 999 | 488 | ||
| gavg t | 2,569 | 2,099 | 1,003 | |
| hypot t | 3,559 | 3,556 | 1,164 | |
| inv | 1,249 | 747 | 242 | 1,253 |
| lambertW0 t | 9,591 | 10,672 | 8,264 | |
| lambertWm1 t | 9,788 | 10,759 | 8,159 | |
| lerp t | 6,890 | 6,515 | 477 | |
| ln t | 3,283 | 3,106 | 1,934 | 16,356 |
| log10 t | 3,426 | 3,101 | 1,923 | |
| log1p t | 2,846 | 2,587 | 2,033 | |
| log2 t | 3,262 | 2,885 | 1,728 | 15,553 |
| log2Up | 1,169 | 679 | 209 | |
| lt | 818 | 233 | 488 | |
| max | 827 | 250 | 113 | |
| min | 837 | 251 | 114 | |
| mul | 1,063 | 530 | 255 | 709 |
| mulDown | 1,244 | 727 | 315 | |
| mulUp | 1,243 | 728 | 337 | |
| neg | 715 | 127 | 116 | 9 |
| normCdf t | 11,442 | 12,354 | 6,316 | |
| pow t | 5,994 | 5,964 | 3,410 | 23,479 |
| powu t | 6,694 | 6,647 | 5,252 | |
| round | 1,100 | 474 | 212 | |
| sign | 764 | 139 | 75 | 181 |
| sinh t | 3,640 | 3,670 | 4,096 | |
| sqrt t | 1,685 | 1,205 | 297 | 760 |
| sub | 1,190 | 745 | 128 | 1,230 |
| tanh t | 3,740 | 3,717 | 3,967 | |
| toBL127 | 914 | 343 | 292 | |
| toFixed18 | 1,030 | 480 | 121 | |
| toFixed18Down | 1,309 | 723 | 160 | |
| toFixed18Up | 1,335 | 724 | 185 | |
| toIntDown | 1,077 | 466 | 278 | |
| toIntUp | 1,124 | 467 | ||
| toUintDown | 871 | 405 | 240 | |
| toUintUp | 937 | 406 | ||
| zeroFloorSub | 1,294 | 829 | 212 |
The function each cell measures
- ABDKMathQuad
absabs(x);addadd(a, b);cmpcmp(a, b);divdiv(a, b);eqeq(a, b);expexp(x);exp2pow_2(x);fromBL127from128x128(x) (truncates; fromBL127 rounds to nearest);fromUintDownfromUInt(x) (truncates the significand);invdiv(fromUInt(1), x) (composed);lnln(x);log2log_2(x);ltcmp(a, b) < 0 (composed);mulmul(a, b);negneg(x);powexp(mul(y, ln(x))) (composed);signsign(x);sqrtsqrt(x);subsub(a, b);toBL127to128x128(x) (truncates; toBL127 rounds to nearest);toIntDowntoInt(x) (truncates: floor for x >= 0, the rows kept);toUintDowntoUInt(x) (truncates: floor for x >= 0)
Median gas per call, 2026-10-28: over random inputs that take no shortcut for the ops marked t, else over the op's input ladder. The object column includes the staticcall; the other three are internal library calls: BL19Lib, the same op in BL127Lib on the same value where 127.128 holds it, and ABDKMathQuad, the other float. An empty cell is an op the library does not offer; rev would be one it offers that reverted, or whose inputs were outside its domain or format, on every input. Hover a Quad cell for the call it measures: lt, inv and pow are composed from Quad's own functions, and its conversions truncate where BL19's round. The WAD libraries and 64.64 are compared on BL127's table. Min and max per cell, and the ladder medians, are in gas.json and gas19.md; every function of every comparison library is mapped in coverage.md.
The source is contracts/src/bl19/BL19.yul.src; the deployed contracts and the inline library are fuzzed equal across all of int256. Full reference: contracts/src/bl19/README.md.