Signature
expm1(float $num): floatPurpose
expm1() returns e raised to the power of a number, minus one. Mathematically that is the same as exp($num) - 1. The difference is in how the computation is done. When $num is very close to zero, exp($num) is almost exactly 1. Subtracting 1 from it then cancels nearly all the significant digits, and what is left is mostly rounding noise. expm1() is designed to avoid that loss, so the result stays accurate for tiny inputs.
The name reads as "exp minus 1". You will see it in numerical code that deals with small rates, small probabilities and growth factors, where the quantity you care about is the change from 1 rather than the value itself.
Signature
Parameters and defaults
$num: the exponent. It is required and has no default value.
Because the parameter is typed float, an integer argument is converted to a float. In the default (non-strict) mode a numeric string such as "0.5" is accepted too. A non-numeric string raises a TypeError, and so does an array. Under declare(strict_types=1) only int and float values are accepted.
Return value and failure cases
The function always returns a float: e to the power of $num, minus one. It does not return false and does not emit warnings for ordinary numbers. The edge cases come from floating-point limits:
expm1(0)is zero.- For very negative inputs the result approaches
-1, and for sufficiently negative values it is-1as a float. - For large positive inputs the true result exceeds what a float can hold, so you get
INF. NANas input givesNAN.
Example 1: basic values and limits
The first example prints the classic e - 1, zero, a very negative input and an overflowing input. Output is formatted with printf() so it does not depend on float display details.
printf("%.6f\n", expm1(1));
printf("%.1f\n", expm1(0));
var_dump(expm1(-1000));
var_dump(is_infinite(expm1(1000)));1.718282 0.0 float(-1) bool(true)
Example 2: why not exp($x) - 1?
For $x = 1e-10, the exact answer is very close to 1.00000000005e-10. The naive subtraction has an absolute error of several times 1e-18, which is huge compared with the answer itself. The expm1() result is accurate to far better than that.
$x = 1e-10;
$expected = 1.00000000005e-10;
$accurate = expm1($x);
$naive = exp($x) - 1;
var_dump(abs($accurate - $expected) < 1e-20);
var_dump(abs($naive - $expected) > 1e-20);bool(true) bool(true)
The comparison uses a tolerance because floats should never be compared with == after calculation. Here the tolerance is much smaller than the error of the naive version, which is what makes the contrast visible.
Example 3: effective rate from a continuous rate
With continuous compounding at rate r, the growth factor over one period is exp(r). The gain is exp(r) - 1, which is exactly what expm1() computes.
$rate = 0.05;
$balance = 1000;
printf("Effective annual rate: %.4f%%\n", expm1($rate) * 100);
printf("Interest on 1000: %.2f\n", $balance * expm1($rate));Effective annual rate: 5.1271% Interest on 1000: 51.27
The interest comes straight from the gain, so there is no need to compute the full future balance and subtract the starting amount.
Edge cases
- Tiny inputs. For very small
$num, the result is approximately$numitself, becausee^x - 1is aboutxnear zero.expm1()keeps the extra digits beyond that approximation. - Large inputs. Overflow produces
INF. Check withis_infinite()if your inputs can be large. - Negative inputs. The result lies between
-1and0. It never goes below-1, becauseexp()is always positive. - Integers.
expm1(1)works the same asexpm1(1.0). The return value is always a float, so do not expect an int. - Mid-range inputs. For values far from zero,
exp($x) - 1andexpm1($x)agree closely, and the accuracy advantage disappears.
Common mistakes
- Forgetting that the result is already minus one. Writing
expm1($x) - 1subtracts twice. Useexpm1($x) + 1if you need the plain exponential back, althoughexp($x)is the better choice then. - Using it for ordinary growth factors. If you need the full factor,
exp()is clearer.expm1()is for the change from one. - Comparing floats with
==. Results are approximations. Compare with a tolerance, or format withprintf()orround()for display. - Passing non-numeric strings. Input from users should be validated first, otherwise a
TypeErrorcan occur. - Ignoring overflow. A large exponent silently becomes
INF, which can then spread through later calculations. - Mixing it up with
log1p(). The two are inverse partners:log1p()is accurate forlog(1 + x)near zero, whileexpm1()is accurate forexp(x) - 1near zero.
Related functions
exp()returnseto the power of a number, without subtracting one.log1p()returnslog(1 + number)with the same near-zero accuracy goal.log()computes natural or custom-base logarithms.pow()raises any base to a power.
Recap
expm1($num) gives exp($num) - 1 as a float, with accuracy preserved when $num is close to zero. Use it when the interesting quantity is a small change from one, such as a rate of gain. Remember that very large inputs overflow to INF, very negative inputs approach -1, and results should be compared with tolerances rather than strict equality.
Tested examples
printf("%.6f\n", expm1(1));
printf("%.1f\n", expm1(0));
var_dump(expm1(-1000));
var_dump(is_infinite(expm1(1000)));1.718282 0.0 float(-1) bool(true)
$x = 1e-10;
$expected = 1.00000000005e-10;
$accurate = expm1($x);
$naive = exp($x) - 1;
var_dump(abs($accurate - $expected) < 1e-20);
var_dump(abs($naive - $expected) > 1e-20);bool(true) bool(true)
$rate = 0.05;
$balance = 1000;
printf("Effective annual rate: %.4f%%\n", expm1($rate) * 100);
printf("Interest on 1000: %.2f\n", $balance * expm1($rate));Effective annual rate: 5.1271% Interest on 1000: 51.27