jsguides

Math.log1p()

Math.log1p(x)

The Math.log1p() function returns the natural logarithm of 1 + x (that is, log_e(1 + x)). The key advantage of this function is numerical precision: when x is very small (close to zero), computing Math.log(1 + x) directly can suffer from floating-point precision loss, while Math.log1p(x) maintains accuracy. This function is particularly useful in financial calculations, scientific computing, and any scenario involving small increments.

Syntax

Math.log1p(x)

These three calls cover the common landmarks: log1p(0) is zero because ln(1) = 0; log1p(1) equals ln(2); and log1p(E-1) is exactly 1 because ln(1 + (E-1)) = ln(E) = 1. The real value of the function, however, only becomes apparent when x is very small.

Examples

Basic usage

console.log(Math.log1p(0));
// 0

console.log(Math.log1p(1));
// 0.6931471805599453 (same as Math.log(2))

console.log(Math.log1p(Math.E - 1));
// 1

The basic inputs above produce straightforward results. To see why log1p exists as a separate function, you need to test values near zero — the range where 1 + x loses precision in 64-bit floating point. The next example compares Math.log(1 + x) against Math.log1p(x) for progressively smaller inputs.

Small values — where log1p shines

const x = 1e-10;

console.log(Math.log(1 + x));
// 9.999999999995e-11 (with potential precision loss)

console.log(Math.log1p(x));
// 9.999999999995e-11 (more accurate)

// For very small values, the difference becomes significant
const tiny = 1e-15;

console.log(Math.log(1 + tiny));
// 0 (floats to 1 due to precision)

console.log(Math.log1p(tiny));
// 9.992...e-16 (still accurate)

Small positive values are the main motivation for log1p, but the function also handles negative inputs safely. As x approaches -1, 1 + x approaches zero and the logarithm approaches -Infinity — behavior that mirrors Math.log while maintaining accuracy along the way.

Negative values

console.log(Math.log1p(-0.5));
// -0.6931471805599453

console.log(Math.log1p(-0.9));
// -2.302585092994046

console.log(Math.log1p(-1));
// -Infinity

Now that the numerical behavior is clear, the practical question is where log1p appears in real code. Financial and statistical calculations involving small percentage changes or probabilities are the most common domains — the compound interest formula below is a canonical example.

Common Patterns

Compound interest with small rates

const effectiveRate = (annualRate, compounds) => {
  return Math.pow(1 + annualRate / compounds, compounds) - 1;
};

// For small rates, use log1p for accuracy
const continuousRate = (annualRate) => {
  return Math.exp(annualRate) - 1;
};

console.log(continuousRate(0.001));
// 0.0010005003335835344

console.log(continuousRate(0.0001));
// 0.0001000050003335833

Compound interest calculations work with rates applied many times per year, where each individual increment is small. A related financial pattern computes log returns from price data, where percentage changes are typically fractions of a percent. Using log1p keeps the calculation accurate even for tiny market movements.

Financial calculations with small increments

const calculateReturn = (initial, final) => {
  // More accurate for small percentage changes
  return Math.log1p((final - initial) / initial);
};

console.log(calculateReturn(1000, 1001));
// 0.000999500333...

console.log(calculateReturn(1000, 1000.5));
// 0.000499833...

Log returns are one side of the coin; the other is information theory, where log1p shows up in entropy formulas. The entropy calculation below uses Math.log1p(p - 1) instead of Math.log(p) because p - 1 is near zero when p is close to 1, and the subtraction preserves more precision than computing the logarithm of p directly.

Probability and information theory

const entropy = (probabilities) => {
  return probabilities.reduce((sum, p) => {
    if (p === 0) return sum;
    return sum - p * Math.log1p(p - 1);
  }, 0);
};

console.log(entropy([0.5, 0.5]));
// 0.6931471805599453

console.log(entropy([0.9, 0.1]));
// 0.3250829733914482

Why precision matters near zero

When x is very small, 1 + x rounds to 1 in 64-bit floating point, and Math.log(1) returns exactly 0. The true answer should be approximately x, but the computation loses all significant digits. Math.log1p(x) avoids this by computing the logarithm directly without first adding 1, using an algorithm that maintains precision for small inputs.

The error becomes significant for x < 1e-8. Below that threshold, always prefer log1p.

const x = 1e-12;
console.log(Math.log(1 + x)); // 9.99...e-13 (but may be 0 for very small x)
console.log(Math.log1p(x));   // 9.99...e-13 (correct)

Precision near zero is why log1p matters, but the function is often paired with its inverse. For any small x, expm1 computes exp(x) - 1 without losing digits, and composing the two functions recovers the original value exactly.

Inverse function: expm1

The companion function Math.expm1(x) computes Math.exp(x) - 1 accurately for small x. The two functions are mathematical inverses: Math.log1p(Math.expm1(x)) === x for small x. They are commonly used together in statistics and numerical analysis:

console.log(Math.expm1(0.001));   // 0.0010005003335835335
console.log(Math.log1p(Math.expm1(0.001))); // 0.001 (back to x)

The log1p-expm1 pair is mathematically neat, but production code must also handle inputs that fall outside the function’s domain. The edge case summary below documents every boundary — zero, -1, negative numbers below -1, NaN, and Infinity — in a single reference block.

Edge cases

Math.log1p(0);    // 0
Math.log1p(-1);   // -Infinity (log of zero)
Math.log1p(-2);   // NaN (log of negative)
Math.log1p(NaN);  // NaN
Math.log1p(Infinity); // Infinity

Values of x less than -1 produce NaN because 1 + x would be negative, and logarithms of negative numbers are not real.

Parameters

ParameterTypeDescription
xnumberA numeric value. Values less than -1 return NaN; -1 returns -Infinity.

When to use log1p vs log

For most inputs where x is not close to zero, Math.log(1 + x) and Math.log1p(x) produce the same result. The difference matters only when x is smaller than roughly 1e-8: at that scale, 1 + x rounds to 1.0 in IEEE 754 double precision and Math.log(1.0) returns 0, discarding all information. Math.log1p avoids this by computing the result using an algorithm that remains accurate as x approaches zero. If your values are always large (far from zero), use whichever reads more clearly; if values can be arbitrarily small, always use log1p.

See Also