expm1
Returns exp(x) - 1 computed accurately even when x is very close to zero.
expm1(float $num): float- Added in PHP 4.1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$num | float | The argument to process, used as the exponent. |
Return value
floate raised to the power of $num, minus one, without the precision loss that exp($num) - 1 suffers near zero.
Errors
Throws a TypeError if $num is not a float or a value that can be coerced to float.
expm1() computes exp($num) - 1 as a single operation. Mathematically the two are identical; numerically they are not, and that difference is the entire reason the function exists.
Why it exists
When $num is small, exp($num) is a number just above 1. A 64-bit float has about 16 significant digits, so exp(1e-10) stores roughly 1.0000000001 and the digits describing the interesting part — the tiny excess over 1 — are the last few it can hold. Subtracting 1 discards all the leading digits and leaves whatever noise survived rounding. This is catastrophic cancellation. At 1e-16 the answer collapses to exactly 0.0. expm1() computes the small quantity directly and keeps full precision.
When to reach for it
Any time you write exp($x) - 1 and $x can be small. Typical cases: converting a small continuously compounded rate into a periodic rate, computing a percentage change from a log return, survival and hazard functions in statistics, and physics formulas where an exponent approaches zero.
For large $num the two forms agree to the last bit, so there is no reason to switch back — just use expm1() whenever the expression has that shape. Its counterpart for logarithms is log1p(), which computes log(1 + $x) with the same protection.
Example
When to use it
- Convert a small continuously compounded interest rate into an effective periodic rate.
- Turn a log return back into a percentage change without losing precision on small moves.
- Evaluate survival or hazard functions in statistics where the exponent approaches zero.
Frequently asked questions
What is the difference between expm1 and exp?
expm1($x) returns exp($x) - 1 as one operation, avoiding the precision loss that happens when you subtract 1 from a number just above 1. For arguments far from zero the two give identical results.
When should I use expm1 instead of exp($x) - 1?
Whenever the argument can be small — small interest rates, log returns, near-zero exponents in statistics. Below about 1e-16 the naive form returns exactly 0.0 while expm1() stays accurate.
What is the logarithm equivalent of expm1?
log1p(), which computes log(1 + $x) accurately for small $x. expm1() and log1p() are exact inverses of each other and are normally used as a pair.
More examples
Where the two forms diverge
At 1e-2 the two agree to every digit shown. By 1e-10 the relative error of exp()-1 is around 1e-7, and at 1e-17 exp()-1 returns 0 while expm1 still reports 1e-17 — a 100% error. The damage grows steadily as the argument shrinks.
<?php
foreach ([1e-2, 1e-6, 1e-10, 1e-14, 1e-17] as $x) {
$a = expm1($x);
$b = exp($x) - 1;
$rel = $a == 0.0 ? 0.0 : abs($b - $a) / abs($a);
printf("x=%-7.0e expm1=%-14.6e exp-1=%-14.6e rel.err=%.2e\n", $x, $a, $b, $rel);
}Continuous rate to effective periodic rate
On a small rate the two effective rates differ in the eleventh significant digit — 0.000000013698630231 against 0.000000013698630230 — a relative error of about 5e-11. Even against a 250 million balance both figures still print as 3.424658, so the discrepancy stays far below a cent here; the reason finance code reaches for expm1() is that the relative error keeps growing as the rate shrinks, until at rates below about 1e-16 the naive form collapses to exactly zero.
<?php
$annual = 0.000005; // 0.0005% continuously compounded
$daily = $annual / 365;
printf("effective daily (expm1): %.18f\n", expm1($daily));
printf("effective daily (naive): %.18f\n", exp($daily) - 1);
$principal = 250_000_000.0; // 250 million
printf("one day interest: %.6f vs %.6f\n",
$principal * expm1($daily), $principal * (exp($daily) - 1));Round-tripping with log1p
log1p() is the inverse of expm1(), and the pair round-trips exactly even at 1e-12. Doing the same with log(1 + (exp($r) - 1)) loses the value entirely at that scale, because both halves cancel independently.
<?php
$rates = [0.05, 1e-6, 1e-12];
foreach ($rates as $r) {
$back = log1p(expm1($r));
printf("r=%-8.0e round-trip=%-12.6e exact=%s\n",
$r, $back, var_export($back === $r, true));
}
Discussion