expm1
MathComputes 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
function expm1(int256 x) internal pure returns (int256 y)Parameters
| Name | Type | Description |
|---|---|---|
| x | int256 | Signed input in 18-decimal fixed-point format (1e18 = 1.0). |
Returns
| Name | Type | Description |
|---|---|---|
| y | int256 | Result e^x − 1 in 18-decimal fixed-point format. Signed — returns negative values for x < 0. |
Bounds
| Bound | Value |
|---|---|
| EXP_UPPER_BOUND | 135.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 seriesx + x²/2! + x³/3! + … + x¹⁰/10!, accurate to ~1e-29 truncation error at the interval boundary. - For
|x| ≥ 0.01: falls through toint256(exp(x)) − 1e18, which has sufficient headroom that the subtraction no longer loses precision. - Inherits exp's bound: reverts with
ExpUpperBoundError()whenx ≥ 135.305999…e18. - For very negative inputs (roughly
x < −41.45e18) returns−1e18— the asymptotic limit ofe^x − 1asx → −∞. - Pure
internalfunction; 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 exp — expm1 returns negative values for x < 0 (e.g. expm1(−1) ≈ −0.632).
Errors
| Error | Trigger |
|---|---|
| ExpUpperBoundError | x ≥ EXP_UPPER_BOUND (positive overflow only — via the internal call to exp; the negative branch silently underflows to −1e18) |
Example
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