expm1

Math

Computes e^x − 1 while preserving full 18-digit precision near zero, where the naive exp(x) − 1 formula catastrophically cancels.

Gas

438

Max rel. error

9.9e-14

Signature

solidity
function expm1(int256 x) internal pure returns (int256 y)

Parameters

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

Returns

NameTypeDescription
yint256Result e^x − 1 in 18-decimal fixed-point format. Signed — returns negative values for x < 0.

Bounds

BoundValue
EXP_UPPER_BOUND135.305999…e18 — inherited from exp on the fallback branch (|x| ≥ 0.01). At x ≥ EXP_UPPER_BOUND the function reverts.
EXP_LOWER_BOUND−41.446531…e18 — also inherited from exp. At x ≤ EXP_LOWER_BOUND the inner exp(x) silently returns 0, so expm1 returns −1e18 (the asymptotic limit of e^x − 1 as x → −∞) — no revert.

Behavior

  • For |x| < 0.01: evaluates a 10-term Taylor series x + x²/2! + x³/3! + … + x¹⁰/10!, accurate to ~1e-29 truncation error at the interval boundary.
  • For |x| ≥ 0.01: falls through to int256(exp(x)) − 1e18, which has sufficient headroom that the subtraction no longer loses precision.
  • Inherits exp's bound: reverts with ExpUpperBoundError() when x ≥ 135.305999…e18.
  • For very negative inputs (roughly x < −41.45e18) returns −1e18 — the asymptotic limit of e^x − 1 as x → −∞.
  • Pure internal function; no external calls or storage.

How it works

Computing e^x − 1 naively as exp(x) − 1e18 is precise everywhere except near zero, where both operands approach 1e18 and the subtraction loses most of its significant digits — classic catastrophic cancellation. At x = 0.001 the true value is ~1.0005e15, but the subtraction exp(0.001e18) − 1e18 can easily drop 15+ digits to rounding noise.

expm1 sidesteps this by switching strategies on the magnitude of x. For |x| < 0.01 we evaluate the Maclaurin series directly:

e^x − 1 = x + x²/2! + x³/3! + x⁴/4! + … + x¹⁰/10!

With 10 terms the truncation error at the interval boundary (|x| = 0.01) is on the order of 0.01¹¹ / 11!2.5e-30, well below 18-digit precision. Every term is computed as an integer multiply-and-divide, no exponential machinery needed — and because each x in the small range stays well-conditioned, no digits are lost to cancellation.

For |x| ≥ 0.01 the cancellation problem disappears: exp(x) and 1 are no longer near-equal, so the function falls through to int256(exp(x)) − 1e18 and inherits exp's ~5e-14 precision. The split point at 0.01 is where the two error profiles meet — below it the Taylor branch dominates, above it the exp branch does.

Note the return type is int256, not uint256 like expexpm1 returns negative values for x < 0 (e.g. expm1(−1) ≈ −0.632).

Errors

ErrorTrigger
ExpUpperBoundErrorx ≥ EXP_UPPER_BOUND (positive overflow only — via the internal call to exp; the negative branch silently underflows to −1e18)

Example

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

int256 x      = 1e15;                  // x = 0.001  (small input — Taylor branch)
int256 result = DeFiMath.expm1(x);     // result ≈ 1.0005e15

int256 y      = 1e18;                  // y = 1.0    (exp fallback branch)
int256 ey     = DeFiMath.expm1(y);     // ey     ≈ 1.71828e18