Mathematics7 min readExamples run on PHP 8.5.10

log1p()

Learn how PHP 8.4 log1p() computes log(1 + num) accurately for tiny values, with examples, edge cases and common mistakes.

Signature

log1p(float $num): float
Available since PHP 4.1.07.48.08.18.28.38.48.5All versions →

Purpose

log1p() returns the natural logarithm of 1 + num. It exists for one reason: precision near zero. When num is very small, the sum 1 + num loses most or all of the digits of num because a float has limited precision. Calling log(1 + $num) then works from an already damaged value. log1p() avoids that intermediate sum, so the result stays accurate.

Typical uses are tiny growth rates, small probabilities, interest or return calculations, and any formula that contains log(1 + x) where x can be very small.

Signature

Parameters and defaults

  • num: the value to process. It is required and has no default. The function computes the logarithm of 1 + num, so the useful domain is num >= -1.

In the default non-strict mode an integer or a numeric string is converted to a float. With declare(strict_types=1), integers are still accepted for a float parameter, but strings are rejected.

Return value and failure cases

The function always returns a float: the value of log(1 + num).

  • log1p(0) gives 0.0.
  • log1p(-1) is the logarithm of zero, which is negative infinity.
  • Any num below -1 would need the logarithm of a negative number, so the result is NAN.
  • A non-numeric string such as "abc" throws a TypeError.

The function does not raise warnings for out-of-domain input. It returns the special float values instead, so check with is_nan() or is_infinite() when the input comes from outside your code.

Example 1: why it beats log(1 + x)

The smallest difference between 1.0 and the next float is about 2.2e-16. Adding 1e-16 to 1 therefore changes nothing, and log() sees exactly 1.

Example
<?php
$x = 1e-16;
var_dump(1 + $x === 1.0);
var_dump(log(1 + $x) === 0.0);
echo round(log1p($x) * 1e16, 6), "\n";
Output
bool(true)
bool(true)
1

The naive version returns exactly zero, which is wrong. log1p() returns a value very close to 1e-16. Multiplying by 1e16 and rounding shows it is essentially 1.

Example 2: log returns and growth

Finance and statistics often work with log returns. A 5% gain is a factor of 1.05, so its log return is log1p(0.05). Log returns can be added together, and expm1() converts the sum back to an ordinary percentage change.

Example
<?php
$returns = [0.10, -0.05, 0.02];
$logs = array_map('log1p', $returns);
echo number_format(log1p(0.05), 6), "\n";
echo number_format(expm1(array_sum($logs)), 6), "\n";
Output
0.048790
0.065900

The second line is the combined change of 1.10 * 0.95 * 1.02 - 1, which is 0.0659. Using number_format() keeps the printed text independent of long float tails.

Example 3: boundaries of the domain

Example
<?php
var_dump(log1p(0));
var_dump(is_infinite(log1p(-1)));
var_dump(log1p(-1) < 0);
var_dump(is_nan(log1p(-2)));
Output
float(0)
bool(true)
bool(true)
bool(true)

Zero maps to zero. Exactly -1 gives negative infinity, and anything lower gives NAN.

Edge cases

  • Large values: for large num, log1p($num) and log(1 + $num) agree to within normal float rounding. The special function only matters near zero.
  • Negative tiny values: log1p(-1e-16) is accurate and slightly negative, while log(1 - 1e-16) can collapse to zero.
  • NAN input: NAN passed in comes back as NAN.
  • Comparing floats: never test results with == unless the value is exactly known, such as 0. Round, or compare with a tolerance.

Common mistakes

  1. Passing the full factor instead of the rate. For a 5% increase, pass 0.05, not 1.05. log1p(1.05) is the logarithm of 2.05.
  2. Using it for other bases. The result is a natural logarithm. For base 10, use log10(1 + $x), which does not have the same near-zero protection.
  3. Ignoring NAN. Values below -1 do not throw. They silently produce NAN, which then spreads through later arithmetic.
  4. Printing raw floats in tests. Output such as 0.04879016416943 can vary with the precision setting. Format with number_format() or round() when exact text matters.
  5. Using it when precision is irrelevant. For moderate values, log(1 + $x) is fine. Use log1p() when x may be tiny.
  • expm1(): the inverse partner, computing exp($num) - 1 accurately near zero.
  • log(): natural logarithm, or logarithm with a chosen base.
  • log10(): base-10 logarithm.
  • exp(): the exponential function.

Recap

log1p($num) returns log(1 + $num) as a float, with accuracy preserved when $num is close to zero. It returns negative infinity for -1 and NAN below -1. Pair it with expm1() for round trips, and pass the rate of change rather than the whole factor.

Tested examples

Naive log(1 + x) loses a tiny value
<?php
$x = 1e-16;
var_dump(1 + $x === 1.0);
var_dump(log(1 + $x) === 0.0);
echo round(log1p($x) * 1e16, 6), "\n";
Output
bool(true)
bool(true)
1
Log returns and converting back with expm1
<?php
$returns = [0.10, -0.05, 0.02];
$logs = array_map('log1p', $returns);
echo number_format(log1p(0.05), 6), "\n";
echo number_format(expm1(array_sum($logs)), 6), "\n";
Output
0.048790
0.065900
Boundaries of the domain
<?php
var_dump(log1p(0));
var_dump(is_infinite(log1p(-1)));
var_dump(log1p(-1) < 0);
var_dump(is_nan(log1p(-2)));
Output
float(0)
bool(true)
bool(true)
bool(true)