exp

Math

Computes the natural exponential of x in 18-decimal fixed-point.

Avg. gas

289

Max abs. error

3.0e-16

when exp(x) < 1

Max rel. error

2.2e-14

when exp(x) ≥ 1

Signature

solidity
function exp(int256 x) internal pure returns (uint256 y)

Parameters

NameTypeDescription
xint256Signed input in 18-decimal fixed-point format (1e18 = 1.0).

Returns

NameTypeDescription
yuint256Result e^x in 18-decimal fixed-point format.

Bounds

BoundValue
EXP_UPPER_BOUND135e18 — positive-input ceiling; at or above it the function reverts. Chosen just below the ~135.306e18 uint256 wrap point so int256(exp(x)) stays safe in expm1.
EXP_LOWER_BOUND−41.446531…e18 — negative-input floor. At x ≤ EXP_LOWER_BOUND the true result is below 1e-18, so the function returns 0 silently (no revert).

Behavior

  • Handles negative inputs internally via reciprocal logic — pass any signed int256.
  • Reverts with ExpUpperBoundError() when x ≥ 135e18.
  • For very negative inputs (roughly x < −41.45e18) returns 0 — a graceful underflow, not a revert.
  • Pure assembly hot path; no external calls or storage.

How it works

The challenge is approximating e^x accurately across a wide input range with only integer arithmetic. DeFiMath applies a two-stage range reduction. Stage 1 splits x = k · ln(2) + r with integer k and r ∈ [0, ln(2)), so e^x = 2^k · e^r — the 2^k factor becomes a free left shift. Stage 2 divides r by 64 (a right-shift by 6): r' = r / 64 ∈ [0, ~0.0108], confining the costly part to a tiny interval.

On that interval a [3,3] Padé approximant approximates e^r' with a handful of multiplies and a single integer division:

e^r' ≈ (120 + 60r' + 12r'² + r'³) / (120 − 60r' + 12r'² − r'³)

The two reductions are then undone in reverse: the result is raised to the 64th power via six successive squarings (y² → y⁴ → … → y⁶⁴) to invert the r / 64 step, then left-shifted by k to apply the 2^k factor. Those squarings amplify the approximant's relative error, so the finished exp holds to a max relative error of 2.2e-14 (and 3.0e-16 absolute near the root x = 0).

Negative inputs use the same machinery on |x|, then reciprocate: exp(−x) = 1 / exp(x). The two endpoints are asymmetric: at x ≥ EXP_UPPER_BOUND (135e18) the function reverts with ExpUpperBoundError. The cap sits just below the ~135.306e18 point where the result would wrap uint256 — the small margin keeps int256(exp(x)) safe inside expm1 even after approximation-error headroom, and exp(135) ≈ 4.3e58 is already astronomically large. At x ≤ EXP_LOWER_BOUND (≈ −41.446e18) the true result is sub-1e-18 — not representable in 18-decimal fixed-point — so the function returns 0 silently as a graceful underflow rather than reverting. The whole hot path stays in unchecked Yul assembly — no library calls, ~289 gas.

Errors

ErrorTrigger
ExpUpperBoundErrorx ≥ EXP_UPPER_BOUND (positive overflow only — the negative branch underflows silently to 0)

Example

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

int256  x = 1e18;             // x = 1.0
uint256 y = Math.exp(x);  // y ≈ 2.71828e18