Skip to content

← JAX Numerical Computing step 24 of 25

Easy Primitives

log1p / expm1 for Small Values

Jump to the task

Keep small changes visible

In float32, a tiny change can disappear when it is added to 1. For example, 1 + 1e-8 rounds to 1, so taking its logarithm gives zero. The intended answer is close to 1e-8.

jnp.log1p(x) computes log(1 + x) without first rounding that sum. Similarly, jnp.expm1(x) computes exp(x) - 1 accurately near zero, where subtracting two nearly equal numbers can lose useful digits. Use these functions when your formula contains those expressions.

Worked example

import jax.numpy as jnp

x = jnp.array(1e-8, dtype=jnp.float32)
naive_log = jnp.log(1.0 + x)  # 0.0
stable_log = jnp.log1p(x)    # approximately 1e-8
naive_exp = jnp.exp(x) - 1.0 # 0.0
stable_exp = jnp.expm1(x)    # approximately 1e-8

The size matters: 1 + 1e-7 does not round to 1 in float32. It rounds to the next representable value above 1. That still introduces a noticeable relative error when calculating the small log.

Round trips have limits

In exact real arithmetic, log1p(expm1(x)) equals x. Floating-point arithmetic only approximates that identity. For moderate inputs the result is close to x, but it need not match bit for bit.

For a large positive float32 input such as 100, expm1 overflows to infinity. For a large negative input such as -100, it rounds to exactly -1, and log1p(-1) returns negative infinity. Neither round trip recovers the original finite input.

On its own, real-valued log1p requires an input greater than -1 for a finite result. At -1 it returns negative infinity, and below -1 it returns NaN. These functions improve numerical accuracy; they do not remove domain restrictions, overflow, or rounding.

Problem

Implement small_value_safe(x) by applying jnp.expm1 to x, then applying jnp.log1p to that result. Return an array with the same shape.

x is a one-dimensional float32 JAX array. For small and moderate finite inputs, expect results close to the inputs within floating-point tolerance. Larger magnitudes can produce the infinities described above. Use the requested functions rather than returning x directly: this exercise is practice composing the two operations.

For example, [0.0, 0.001, 0.5] returns approximately [0.0, 0.001, 0.5]. Empty input produces empty output.