Math

Low-level fixed-point primitives in 18-decimal format (1e18 = 1.0). All pure, gas-efficient, and validated to sub-1e-12 relative error against reference libraries.

Contract: Math.sol

Functions

FunctionGasDescription
exp331Exponential function e^x
expm1438e^x − 1 (precision-preserving for small x)
ln375Natural logarithm
log1p500ln(1 + x) (precision-preserving for small x)
log2391Base-2 logarithm
log10391Base-10 logarithm
pow748Power function x^a
sqrt245Square root
cbrt368Cube root
sqrtTime184Specialized sqrt of time in years for Black-Scholes — no input validation
stdNormCDF660Standard normal CDF Φ(x)
erf685Error function
mulDiv155(a · b) / d with full 512-bit intermediate precision
mul130(a · b) / 1e18 — fixed-point multiply with denominator baked in
abs17Branchless |int256| (handles int256.min cleanly)
min23Branchless minimum of two uint256
max23Branchless maximum of two uint256
clamp78Clamp x into [lo, hi] (composed max then min)
avg21Overflow-safe (a + b) / 2 via (a & b) + ((a ^ b) >> 1)

npm install defimath-lib

Conventions

  • All values use 18-decimal fixed-point (1e18 = 1.0).
  • Signedness mirrors the math: exp takes int256, returns uint256; ln takes uint256, returns int256.
  • All functions are internal pure — no storage access, no external calls.

Quick example

solidity
import "defimath-lib/contracts/math/Math.sol";

uint256 x    = 2e18;                     // x = 2.0
uint256 root = DeFiMath.sqrt(x);         // root ≈ 1.41421e18
int256  lnX  = DeFiMath.ln(x);           // lnX  ≈ 0.69315e18
uint256 ePow = DeFiMath.exp(int256(x));  // ePow ≈ 7.389e18

Important notes

  • Use expm1 for small x. Computing ex − 1 via exp(x) - 1e18 catastrophically cancels when |x| < 0.01. expm1 uses a Taylor series in that range and preserves full 18-digit precision.
  • Use log1p for small x. Same reason — log1p(x) preserves precision near zero where ln(1 + x) would lose ~15 digits to subtraction.
  • Negative exp underflows silently to 0. For x < −41.45e18, exp(x) returns 0 (not a revert) since the true value is below 1e-18 representational precision.
  • CLZ requires Solidity 0.8.31 + EVM "osaka". ln, sqrt, cbrt, and sqrtTime emit the new CLZ opcode introduced in Osaka.
  • sqrtTime is specialized, not general-purpose. It expects x as time in years (1e18 = 1 year) and performs no input validation. It is built for Black-Scholes option pricing — where the caller has already bounded x — and is precision-tuned for [1s, 32y]. For a general fixed-point square root use sqrt instead.

Every function reverts on out-of-bounds inputs with a named error — see the per-function pages for limits and error specifics.

Testing

Hardhat correctness layer. 202 tests across 20 function groups. Each function is validated against the corresponding JavaScript reference (Math.exp, Math.log, Math.sqrt, math-erf) over ~200-sample sweeps that cover the full operational domain. Per-function precision thresholds match the table above. Limit tests pin behavior at min and max valid inputs; failure tests cover every documented revert path.

Foundry property-fuzz layer. 28 mathematical properties, each fuzzed with 32,000 random inputs per CI run (= 896,000 random executions). Counterexamples are automatically shrunk on failure.

CategoryCountWhat they check
Round-trips5ln(exp(x)) ≈ x, exp(ln(x)) ≈ x, sqrt²(x) ≈ x, cbrt³(x) ≈ x, exp(x)·exp(-x) ≈ 1
Monotonicity5exp, ln, sqrt, Φ, erf all preserve input ordering
Identities8expm1 = exp − 1, log1p = ln(1 + x), pow at exponents 0/1/2, mul = mulDiv(_, _, 1e18), mulDiv = a·b/d, min + max = a + b, avg = (a + b)/2
Output bounds6Φ ∈ [0, 1], erf ∈ [-1, 1], pow always non-negative, min ≤ max, clamp ∈ [lo, hi], avg ∈ [min, max]
Symmetries3Φ(x) + Φ(-x) = 1, erf(-x) = -erf(x), abs(-x) = abs(x)

Sources: test/Math.test.mjs · test/foundry/Math.t.sol