blockulator
BL19BL127ABDKMathQuadPRBMath SD59x18Solady
digits7138341818
add6621287341893
mul530267709485126
div80041381850993
exp t3,2782,8355,6682,816430
ln t3,1061,94416,3567,083556
pow t5,9643,31723,47910,0651,279
sqrt t1,205469760978393

Inline library, median gas per call, over random inputs that take no shortcut for the ops marked t and over the input ladder for the rest; digits are precision at 1. BL19 is cheaper than Quad on add, mul, div, exp, ln and pow, not on sqrt. BL127 is cheaper than PRBMath on add, mul, div, ln, pow and sqrt, not on exp. Solady is cheaper than both on every op here, at 18 digits; its add is the unchecked rawAdd. Sources: bl19/gas.json (BL19, Quad), gas.json (the rest).

Only BL19 and BL127 offer erf, erfc, normCdf and both Lambert W branches (Solady has W₀ alone), each correctly rounded or the nearest word. BL19's per-op detail, with the deployed objects, is on its page.

Gas per op

Every op of BL19 and BL127 against every library we compare, all called inline.

opBL19BL127ABDKMathQuadPRBMathSoladyABDK 64.64BalancerSolmatePRB UD60x18snekmateOpenZeppelinUniswap
abs133140211211812018
add6621287341893162150
atanh t3,8822,944
avg6631141546031105113318
cbrt t2,7341,9366192,3131,424
ceil523147201117
clamp49124149
cmp248476
compound t5,8583,078
cosh t3,6924,073
dist81928932
div8004138185099322197261
divDown98747687150323239179
divUp988479113176386621282
eq176108
erf t10,6247,559
erfc t10,6547,186
exp t3,2782,8355,6682,8164303,3801,0404302,720604
exp10 t3,1773,081
exp2 t3,0962,7125,0312,6713,2252,557
expm1 t2,3952,996
factorial719400579
floor5227220128
frac428691258
fromBL127440539
fromFixed18554140
fromUint413
fromUintDown487500
fromUintUp488
gavg t2,0991,003960449833949
hypot t3,5561,164
inv7472421,2531779320294
lambertW0 t10,6728,3043,045
lambertWm1 t10,7598,156
lerp t6,515477282
ln t3,1061,94416,3567,0835567,0801,0916326,4741,226
log10 t3,1011,9348,6181,8958,233
log1p t2,5872,064
log2 t2,8851,72315,5536,7246,9901,9146,402
log2Up679209247505513
lt233488
max2501252386
min2511262386
mul530267709485126171131237
mulDown72731586150323239179
mulUp728337110176386621282
neg127116913476
normCdf t12,3546,037
pow t5,9643,31723,47910,0651,2792,2231,3559,544
powu t6,6475,2524,0062,3183,1332,5593,881
round474212
sign139751818478
sinh t3,6704,109
sqrt t1,2054697609783938282,351456824766
sub7451401,2301873162150
tanh t3,7173,975
toBL127343292
toFixed18480121
toFixed18Down723160
toFixed18Up724185
toIntDown466278
toIntUp467
toUintDown405240
toUintUp406
zeroFloorSub8292121792
The function each cell measures
ABDKMathQuad
abs abs(x); add add(a, b); cmp cmp(a, b); div div(a, b); eq eq(a, b); exp exp(x); exp2 pow_2(x); fromBL127 from128x128(x) (truncates; fromBL127 rounds to nearest); fromUintDown fromUInt(x) (truncates the significand); inv div(fromUInt(1), x) (composed); ln ln(x); log2 log_2(x); lt cmp(a, b) < 0 (composed); mul mul(a, b); neg neg(x); pow exp(mul(y, ln(x))) (composed); sign sign(x); sqrt sqrt(x); sub sub(a, b); toBL127 to128x128(x) (truncates; toBL127 rounds to nearest); toIntDown toInt(x) (truncates: floor for x >= 0, the rows kept); toUintDown toUInt(x) (truncates: floor for x >= 0)
PRBMath
abs abs(x); add add(a, b); avg avg(a, b); ceil ceil(x); div div(a, b); exp exp(x); exp2 exp2(x); floor floor(x); frac frac(x); gavg gm(a, b); inv inv(x); ln ln(x); log10 log10(x); log2 log2(x); mul mul(a, b); neg unary(x); pow pow(x, y); powu powu(x, n), n a uint256 (n >= 0); sign sign(x); sqrt sqrt(x); sub sub(a, b)
Solady
abs abs(int256); add rawAdd(int256, int256) (unchecked); avg avg(int256, int256); cbrt cbrtWad(x) (x >= 0); clamp clamp(int256, int256, int256); dist dist(int256, int256); div sDivWad(a, b); divDown divWad(a, b) (a >= 0, b > 0); divUp divWadUp(a, b) (a >= 0, b > 0); exp expWad(x); factorial factorial(uint256) on the integer x (integral x); gavg mulSqrt(a, b) (a, b >= 0); inv sDivWad(1e18, x) (composed); lambertW0 lambertW0Wad(x); lerp lerp(a, b, t, 0, 1e18) (t in [0, 1]: Solady clamps t); ln lnWad(x); log2Up log2Up(uint256) on the integer x (integral x >= 1); max max(int256, int256); min min(int256, int256); mul sMulWad(a, b) (toward zero); mulDown mulWad(a, b) (a, b >= 0); mulUp mulWadUp(a, b) (a, b >= 0); pow powWad(x, y); powu rpow(x, n, 1e18) (x >= 0, n >= 0); sqrt sqrtWad(x) (x >= 0); sub rawSub(int256, int256) (unchecked); zeroFloorSub zeroFloorSub(uint256, uint256) (a, b >= 0)
ABDK 64.64
abs abs(x); add add(a, b); avg avg(a, b); div div(a, b); exp exp(x); exp2 exp_2(x); gavg gavg(a, b); inv inv(x); ln ln(x); log2 log_2(x); mul mul(a, b); neg neg(x); powu pow(x, n), n a uint256 (n >= 0); sqrt sqrt(x); sub sub(a, b)
Balancer
cbrt pow(x, 333333333333333333) (composed; x >= 0); exp exp(x); ln ln(x); log10 log(x, 10e18); log2 log(x, 2e18); pow pow(x, y) (x, y >= 0); sqrt pow(x, 0.5e18) (composed; x >= 0)
Solmate
div wadDiv(a, b) (toward zero); divDown divWadDown(a, b) (a, b >= 0); divUp divWadUp(a, b) (a, b >= 0); exp wadExp(x); ln wadLn(x); mul wadMul(a, b) (toward zero); mulDown mulWadDown(a, b) (a, b >= 0); mulUp mulWadUp(a, b) (a, b >= 0); pow wadPow(x, y); powu rpow(x, n, 1e18) (x >= 0, n >= 0); sqrt sqrt(x * 1e18) (composed: the integer root; x >= 0)
PRB UD60x18
add add(a, b) (a, b >= 0); avg avg(a, b) (a, b >= 0); ceil ceil(x) (x >= 0); div div(a, b) (a, b >= 0); exp exp(x) (x >= 0); exp2 exp2(x) (x >= 0); floor floor(x) (x >= 0); frac frac(x) (x >= 0); gavg gm(a, b) (a, b >= 0); inv inv(x) (x >= 0); ln ln(x) (x >= 0); log10 log10(x) (x >= 0); log2 log2(x) (x >= 0); mul mul(a, b) (a, b >= 0); pow pow(x, y) (x, y >= 0); powu powu(x, n), n a uint256 (x >= 0, n >= 0); sqrt sqrt(x) (x >= 0); sub sub(a, b) (a, b >= 0)
snekmate
avg _int256_average(a, b); cbrt _wad_cbrt(x) (x >= 0); divDown _mul_div(a, 1e18, b, False) (a, b >= 0); divUp _mul_div(a, 1e18, b, True) (a, b >= 0); exp _wad_exp(x); ln _wad_ln(x); log2Up _log2(uint256, True) on the integer x (integral x >= 1); mulDown _mul_div(a, b, 1e18, False) (a, b >= 0); mulUp _mul_div(a, b, 1e18, True) (a, b >= 0); sign _signum(x)
OpenZeppelin
abs SignedMath.abs(int256); avg SignedMath.average(a, b) (toward zero; BL127's avg floors); divDown Math.mulDiv(a, 1e18, b) (a, b >= 0); divUp Math.mulDiv(a, 1e18, b, Rounding.Ceil) (a, b >= 0); log2Up Math.log2(uint256, Rounding.Ceil) on the integer x (integral x >= 1); max SignedMath.max(a, b); min SignedMath.min(a, b); mulDown Math.mulDiv(a, b, 1e18) (a, b >= 0); mulUp Math.mulDiv(a, b, 1e18, Rounding.Ceil) (a, b >= 0); sqrt Math.sqrt(x * 1e18) (composed: the integer root; x >= 0); zeroFloorSub Math.saturatingSub(uint256, uint256) (a, b >= 0)
Uniswap
divDown FullMath.mulDiv(a, 1e18, b) (a, b >= 0); divUp FullMath.mulDivRoundingUp(a, 1e18, b) (a, b >= 0); mulDown FullMath.mulDiv(a, b, 1e18) (a, b >= 0); mulUp FullMath.mulDivRoundingUp(a, b, 1e18) (a, b >= 0)

Median gas per inline call: over random inputs that take no shortcut for the ops marked t, else over the op's input ladder. BL19 and ABDKMathQuad are measured on the same values (bl19/gas.json), the others on BL127's inputs (gas.json). Empty: the library does not offer the op; rev: it offers it and reverted on every input. Hover a cell for the function it calls. Calling the deployed contract instead adds the staticcall: about 554 gas on BL127's add (682 against 128); every op's object figure is on the BL127 and BL19 pages.

Precision per op

opvectorsrevertsmin bitsmean bitsworst input
abs981exactexact
add37723exactexact
atanh5345130131
0x000000000000…0x0000000000000000000000000000000000000b504f333f9de6484597d89b3754
avg2000129234
0xffffffffffff…0xfffffffffffffffff21f494c589c000000000000000000000000000000000000,0xfffffffffffffffffffffffffffffd34d62c1fdb28540383534af43c13a51359
cbrt981130164
0x000000000000…0x00000000000000001bce1abd36207803b5f3bb79b2f817e5a2d5e288b8fe5d96
ceil972exactexact
clamp3000exactexact
compound14159130208
0x000000000000…0x0000000000000000000000000000000019999999999999999999999999999999,0x0000000000000000000000000000016d00000000000000000000000000000000
cosh5754129175
0x000000000000…0x0000000000000000000000000000000000000000000000010000000000000000
dist19010exactexact
div37723129193
0x23fccae3cfdc…0x23fccae3cfdca92920400cceaf07264105d9fd08f1256e2392022e9d1745ab4a,0x00000000000000000000000000000003243f6a8885a308d313198a2e03707344
divDown36337128188
0xffffffffffff…0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,0x7870107c047009ebaad71c7ebbaf317671aaa8a105a47733a492aebe56be3702
divUp36733129190
0x000000000000…0x0000000000000000000000000000000180000000000000000000000000000000,0x00000000000000000000000000000000004189374bc6a7ef9db22d0e56041893
erf5840132186
0x000000000000…0x000000000000000000000000000000029a7e647802374d98add579a7fcc24210
erfc5840130132
0x000000000000…0x000000000000000000000000000000029a7e647802374d98add579a7fcc24210
exp1174130207
0xffffffffffff…0xffffffffffffffffffffffffffffffa6947ae147ae147ae147ae147ae147ae14
exp10803131217
0xffffffffffff…0xfffffffffffffffffffffffffffffff141f0353d3320c8f151d83c6c95e647d4
exp21043130217
0xffffffffffff…0xffffffffffffffffffffffffffffff7f00000000000000000000000000000000
expm15772130153
0x000000000000…0x0000000000000000000000000000000000000000000000010000000000000000
factorial126exactexact
floor990exactexact
frac990exactexact
fromFixed18334131191
0xffffffffffff…0xfffffffffffffffffffffffffffffffff5cc03f34ac9aaa4896da2872a07e2d0
gavg2000131210
0x000000000000…0x0000000000000000000000000000000000000000000000000000000000000400,0x0000000000000000000000000000000000000000000000000000800000000000
hypot1210131183
0x000000000000…0x0000000000000000000000000000000080000000000000000000000000000000,0x00000000000000000000000000000003243f6a8885a308d313198a2e03707344
inv623exactexact
0x203165f4c782…0x203165f4c782db9cffce938726dd1ec020de05fd184a1af4395e2a8402671297
lambertW01403130134
0xffffffffffff…0xffffffffffffffffffffffffffffffffa3d70a3d70a3d70a3d70a3d70a3d70a3
lambertWm1866130131
0xffffffffffff…0xffffffffffffffffffffffffffffffffb30b072074fe3b487073a1b28c4adf68
lerp28911exactexact
ln3224130135
0x000000000000…0x0000000000000000000000000019eeee1991f56daa32cdc95baeb71517c3c017
log103224130135
0x000000000000…0x00000000000000000000000000000000005fa366409a1e0f1037a02f8b4a8778
log1p56118130132
0x000000000000…0x0000000000000000000000000000000000000000000000010000000000000000
log23224130233
0x000000000000…0x0000000000000000028dfc4db50a10ff45a3860e8964d6a14a662cfe41b5139e
log2Up3224exactexact
max2000exactexact
min2000exactexact
mul231169132232
0xffffffffffff…0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,0x8000000000000000000000000000000000000000000000000000000000000001
mulDown218182131229
0x000000000000…0x0000000000000000000000000000000000000000000000000000000000000002,0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe
mulUp225175129226
0xffffffffffff…0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe,0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
neg981exactexact
normCdf5840147228
0x000000000000…0x00000000000000000000000000000004bba8d97f090a853b60da8e5e13acf310
pow50199130181
0x000000000000…0x0000000000000000000000000000017b8003884dd86c89a488fc31f62f8cea98,0xfffffffffffffffffffffffffffffffee80023779f2d390dbf519c550fe6719a
powu435165130204
0xffffffffffff…0xfffffffffffffffffffffffffffffffc29a12310624d183ca1d03b091fb77270,0x0000000000000000000000000000004000000000000000000000000000000000
round972exactexact
sign990exactexact
sinh5754130149
0x000000000000…0x0000000000000000000000000000000000016a09e667f3bcc908b2fb1366ea95
sqrt2983129198
0x000000000000…0x0000000000000000000000000000000000000000000000000000000000020000
sub38020exactexact
tanh5790130216
0x000000000000…0x00000000000000000000000000000000005a827999fcef32422cbec4d9baa55f
toFixed18990181199
0xffffffffffff…0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
toFixed18Down990181199
0xffffffffffff…0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
toFixed18Up990182201
0x000000000000…0x0000000000000000000000000000000000000000000000000000000000000001
zeroFloorSub18911exactexact

Relative correct bits of the 127.128 result, msb(|truth|) − msb(|truth − got|), against the real truth (mpmath at 200 decimal places) over the committed vectors in test/bl127/vectors/, for results of magnitude 1 or more. "exact" is bit-identical with the truth. Every op is also held to its rounding contract: nearest within half an ULP, floor, ceil and trunc within one; maxErrUlp in the data. Reverts counts vectors where the oracle and the Yul both reject the input with the same error. Hover a worst input for the whole word. Source: precision.json.

  • Integer, rounding and comparison ops: bit-exact. mul and sqrt round down and div toward zero, within one ULP; "exact" in the table means equal to an oracle that rounds the same way. The directed ops round the way their names say, within one ULP.
  • Every transcendental (exp, ln, pow, powu, compound, cbrt, the hyperbolics, erf, erfc, normCdf, both Lambert W branches) is correctly rounded to nearest: each fast value carries a derived error bound and a rounding test, and a wider stage decides what the test cannot, so the word is within half an ULP (maxErrUlp in the data) at every committed vector and on every random input of the sweep, plain and with every test forced. exp(ln x) returns the same word for 10,000 steps. The pow exception of earlier releases (an exponent near 290) is gone in 3.0.