Contents
Deep Breakdown
What it tells me (Situation Awareness):
This limit is the derivative of at : . It says that for small — the exponential grows at rate 1 near 0. This is why is the "natural" base: it is the unique base where this derivative equals exactly 1. Equivalently: confirms that , since at the slope is . In DS/DA: the numerically stable computation of for small (use np.expm1(x) to avoid catastrophic cancellation); log-return approximation in finance ( for small , derived from the companion limit ); Taylor expansion of the exponential used in gradient approximations.
Flowchart
Real-world use
Where it is used
Proving from first principles; numerical stability: np.expm1(x) computes accurately for small (avoids catastrophic cancellation in when ); log-return approximation: for small return , (companion limit ); soft-plus gradient: — uses the fact that for the slope analysis; small-perturbation analysis of any system that uses as an activation or link function.
Problem it solves
is undefined, yet the slope of at 0 is clearly well-defined. This limit provides that slope, confirming has slope 1 at . It also drives numerically stable computation: for small , , so but the naive subtraction loses precision due to floating-point cancellation.
How it solves the problem
Taylor expansion: . Divide by : as . Alternatively, L'Hôpital: .
Example in a business
A DS computes log-returns for a financial time series. For a price relative where is the simple return, the log-return is . For small (e.g., daily returns ): . The approximation error is : at (1%), error . This approximation is valid because (the companion limit to the exponential standard limit).
The formula, piece by piece
- : exponential function base
- : the exponential minus its value at (which is 1)
- : the increment from 0
- : "how much does increase per unit of near 0?" — the slope at 0
- : the slope of at is 1, consistent with
- Generalization: ;
- Companion: (inverse relationship: and cancel near 0)
- Numerical note:
np.expm1(x)is the numerically stable version of for small
The formula in words
"For small , is approximately . So , and the ratio approaches 1. This says the exponential function has slope 1 at the origin." Tiny trace: : . : . : .
Critical Concepts
- This is at : the derivative definition gives . Combined with the general formula (chain rule with ), this limit confirms the entire derivative. It is the anchor point.
np.expm1(x)for numerical stability: for small ,np.exp(x) - 1suffers catastrophic cancellation: , and subtracting 1 in float64 loses all significant digits.np.expm1(x)computes via a Taylor series for small , returning approximately with full precision. Always useexpm1when computing for potentially small (e.g., Poisson log-likelihood, option pricing, softplus gradient).- Log-return and log-sum-exp: the companion limit (proved by L'Hôpital: ) means log-returns simple returns for small returns. This is used in finance, information theory (relative entropy: for small differences), and in log-sum-exp numerics.
- Taylor expansion and ML activations: for small . The softplus function : for , . Its derivative : at , . Both rely on the slope-1 property at 0.
- Relationship to interest rate formulas: for a fractional return per period, after periods the total is .
For small : using . This is why the rule of 72 works: at annual rate , doubling time uses ; in percentage terms, (rounded for convenience).
Step-by-step calculation (tiny example)
Evaluate .
Step 1: Introduce a factor of 3. .
Step 2: Let ; as , . .
Step 3: Apply the standard limit. . Verify: : .
Code examples
Beginner example (Python)
import numpy as np
print("Standard Limit: lim_{x->0} (e^x - 1)/x = 1")
print(f"\n{'x':>12} {'(e^x-1)/x':>14} {'Error from 1':>14}")
for x in [0.5, 0.1, 0.01, 0.001, 0.0001, 1e-8]:
ratio = np.expm1(x) / x # numerically stable
error = abs(ratio - 1.0)
print(f"{x:>12.6e} {ratio:>14.9f} {error:>14.2e}") # Generalization: lim(e^(ax) - 1)/x = a
print(f"\n--- Generalization: lim_{{x->0}} (e^(ax)-1)/x = a ---")
print(f"{'a':>6} {'Numerical (x=1e-8)':>20} {'Analytic (=a)':>14}")
for a in [0.5, 2.0, -1.0, np.pi]:
x = 1e-8
numerical = np.expm1(a * x) / x
print(f"{a:>6.3f} {numerical:>20.8f} {a:>14.6f}") # Numerical stability: expm1 vs naive subtraction
print(f"\n--- Numerical stability: expm1 vs exp(x)-1 for tiny x ---")
print(f"{'x':>12} {'exp(x)-1 (naive)':>20} {'expm1(x) (stable)':>20} {'True (approx x)':>18}")
for x in [1e-6, 1e-10, 1e-14, 1e-16]:
naive = np.exp(x) - 1 # suffers cancellation for very small x
stable = np.expm1(x) # uses Taylor series near 0
print(f"{x:>12.2e} {naive:>20.15e} {stable:>20.15e} {x:>18.15e}")Beginner example (R)
# Standard limit: lim_{x->0} (e^x - 1)/x = 1
cat("Standard Limit: lim_{x->0} (e^x - 1)/x = 1\n\n")
cat(sprintf("%-12s %-14s %-14s\n", "x", "(e^x-1)/x", "Error from 1"))
for (x in c(0.5, 0.1, 0.01, 0.001, 1e-8)) {
ratio <- expm1(x) / x # expm1 is available in base R
error <- abs(ratio - 1.0)
cat(sprintf("%-12.2e %-14.9f %-14.2e\n", x, ratio, error))
}
# Log-return approximation: log(1+r) ≈ r for small r
cat("\n--- Log-return approximation: log(1+r) ≈ r for small r ---\n")
cat(sprintf("%-8s %-12s %-12s %-12s\n", "r (%)", "log(1+r)", "r (approx)", "Error"))
for (r_pct in c(10, 5, 1, 0.1, 0.01)) {
r <- r_pct / 100
lr <- log(1 + r) # log1p is also available
err <- abs(lr - r)
cat(sprintf("%-8.2f %-12.6f %-12.6f %-12.4e\n", r_pct, lr, r, err))
}Professional example (Python)
import numpy as np
from sklearn.datasets import make_classification
from sklearn.preprocessing import StandardScaler
np.random.seed(42)
# -- Standard Limit (exp): numerically stable Poisson log-likelihood --
# Use case: A DS fits a Poisson regression model manually and encounters
# the numerical stability issue with computing log(lambda) when lambda is
# very small. The expm1 function (based on the standard exponential limit)
# ensures stable computation of e^x - 1 for small x.
#
# Poisson log-likelihood: ell(w) = sum_i [y_i * (w^T x_i) - exp(w^T x_i)]
# For link: lambda_i = exp(w^T x_i)
# Gradient: d(ell)/dw = X^T (y - lambda)
# Key issue: when w^T x is large and negative, lambda ≈ 0, and computations
# like y*log(lambda) need care.
N = 200
DIM = 3
LR = 0.01
N_ITER = 300
def poisson_log_lambda(w: np.ndarray, X: np.ndarray) -> np.ndarray:
"""Log of Poisson rate: z = X @ w. lambda = exp(z)."""
return X @ w # log(lambda) directly
def stable_poisson_ll(w: np.ndarray, X: np.ndarray, y: np.ndarray) -> float:
"""
Numerically stable Poisson log-likelihood.
ell = sum[y * log_lambda - exp(log_lambda)] = sum[y * z - exp(z)]
Uses np.exp with clipping to avoid overflow.
"""
z = poisson_log_lambda(w, X)
lam = np.exp(np.clip(z, -30, 20))
return float(np.sum(y * z - lam))
def poisson_gradient(w: np.ndarray, X: np.ndarray, y: np.ndarray) -> np.ndarray:
"""Gradient: d(ell)/dw = X^T(y - lambda)."""
z = poisson_log_lambda(w, X)
lam = np.exp(np.clip(z, -30, 20))
return X.T @ (y - lam)
# Demonstrate expm1 precision for small-lambda regime
print("Standard Limit (exp): expm1 Precision for Small Lambda")
print(f"\nlim_{{x->0}} (e^x - 1)/x = 1 -> e^x - 1 ≈ x for small x")
print(f"\nNumerical stability of e^x - 1 in Poisson computations:")
print(f"{'z (log lambda)':>18} {'lambda':>12} {'naive exp(z)-1':>18} {'expm1(z)':>12} {'True ~ z':>10}")
for z in [-10, -5, -1, -0.1, -0.01, -0.001, -1e-6, -1e-10]:
lam = np.exp(z)
naive = np.exp(z) - 1
stable = np.expm1(z)
print(f"{z:>18.4e} {lam:>12.6e} {naive:>18.10e} {stable:>12.10e} {z:>10.6e}")
# Fit Poisson regression via gradient ascent on log-likelihood
X_raw = np.random.randn(N, DIM)
scaler = StandardScaler()
X = scaler.fit_transform(X_raw)
true_w = np.array([0.5, -0.3, 0.2])
log_lam_true = X @ true_w
y = np.random.poisson(np.exp(np.clip(log_lam_true, -5, 5))).astype(float)
w = np.zeros(DIM)
ll_history = []
for step in range(N_ITER):
ll = stable_poisson_ll(w, X, y)
ll_history.append(ll)
grad = poisson_gradient(w, X, y)
w = w + LR * grad / N
print(f"\nPoisson Regression via Gradient Ascent (max-likelihood)")
print(f"N={N}, DIM={DIM}, true_w={true_w}")
print(f"\nLog-likelihood: step 1={ll_history[0]:.2f}, step 100={ll_history[99]:.2f}, "
f"final={ll_history[-1]:.2f}")
print(f"Fitted w: {np.round(w, 4)}")
print(f"True w: {true_w}")
print(f"\nKey: d/dw[exp(w^Tx)] = exp(w^Tx)*x -- uses d/dx[e^x] = e^x, anchored by lim(e^x-1)/x = 1")