In early 2020 a lot of people learned the phrase "doubling every three days." A month later the same commentators were saying "growth is slowing." The math didn't change. What changed is that the quantity being measured ran into a ceiling — and every unbounded growth curve you'll ever plot a metric against does the same thing, whether it's infections, DAU, or a tweet's retweet count.
This post walks through why, with formulas you can verify on a calculator and a script short enough to paste into a REPL.
Exponential growth is a multiplication rule, not a speed
The discrete form is:
y = a * b**t
a is the starting value, b is the growth factor per period, t is the number of periods. If b = 2, the quantity doubles every period. The continuous form uses e:
y = a * e**(r*t)
where r is the continuous growth rate and b = e**r. Both describe the same property: the rate of change is proportional to the current size. That's the entire definition — nothing in it says "fast." A process growing at r = 0.001 per year is exponential in structure but you'd never notice it happening. Media shorthand for "exponential" as "extremely fast" is a category error; exponential means "multiplicative," full stop.
Doubling time, computed, not looked up
Doubling time is how many periods it takes for y to hit 2a. Solve a * e**(r*t) = 2a for t and you get:
import math
def doubling_time(r):
return math.log(2) / r
for r in [0.01, 0.05, 0.10, 0.25, 0.50, 1.00]:
print(f"r={r:.2f} -> {doubling_time(r):.2f} periods to double")
Running that gives:
| Growth rate (r) | Doubling time |
|---|---|
| 1% | 69.31 periods |
| 5% | 13.86 periods |
| 10% | 6.93 periods |
| 25% | 2.77 periods |
| 50% | 1.39 periods |
| 100% | 0.69 periods |
The "Rule of 70" shortcut (divide 70 by the percentage rate) is just ln(2) ≈ 0.693 ≈ 0.70 rounded for mental math — at 7% growth, 70/7 = 10 periods, which matches ln(2)/0.07 = 9.9. Nothing mystical, just a rounding convenience.
Why the curve has to bend
The exponential model has a hidden assumption: unlimited room to grow. An infected person always finds a susceptible one, a shared post always finds a new viewer. That assumption is false in every bounded system, which is every real system. Once you add a ceiling — call it K, the carrying capacity — the growth rate has to fall as you approach it. That's the logistic model:
def logistic(n0, r, k, t):
return k / (1 + ((k - n0) / n0) * math.exp(-r * t))
dN/dt = r * N * (1 - N/K) is the differential-equation form. The (1 - N/K) term is a brake: near zero it's close to 1 (so growth looks purely exponential), and as N approaches K it drops toward 0 (so growth stops). This is exactly why early-stage growth of almost anything — a pandemic, a viral post, a new product — looks exponential: locally, before N is a meaningful fraction of K, the brake term is doing nothing yet.
Watching the two models diverge
Same starting conditions, run both models out over 70 periods:
n0, k, r = 100, 1_000_000, 0.3
def exponential(a, r, t):
return a * math.exp(r * t)
for day in [0, 10, 20, 30, 40, 50, 60, 70]:
e = exponential(n0, r, day)
l = logistic(n0, r, k, day)
print(f"day {day:2d}: exponential={e:,.0f} logistic={l:,.0f}")
By day 30 the two curves are still close — the exponential model hasn't broken anything yet. By day 70, the exponential model predicts roughly 1.3 * 10**11 — about 130,000 times the entire population you defined as K = 1,000,000. The logistic model, run with the identical r, is sitting at roughly 999,992: essentially saturated. Nothing about the underlying process changed between the two models except the brake term. The exponential model isn't wrong because the math is bad; it's wrong because it was never told there's a ceiling.
You can solve for exactly when the curve bends — the inflection point, where the growth rate peaks — by setting N = K/2 and solving for t:
t* = ln((K - N0) / N0) / r
For the numbers above (N0 = 100, K = 1,000,000, r = 0.3), that's ln(9999) / 0.3 ≈ 30.7 days. Before that day, each new period's absolute growth is still accelerating. After it, growth keeps happening but the rate of growth is falling — even though the raw numbers can still look large for a while.
Exponential vs. logistic, side by side
| Property | Exponential | Logistic |
|---|---|---|
| Formula | y = a·e^(rt) |
dN/dt = rN(1 − N/K) |
| Growth rate | Constant r
|
Falls as N → K
|
| Long-run shape | Unbounded (J-curve) | Saturates at K (S-curve) |
| Inflection point | None — always accelerating | At N = K/2
|
| Where it's a good model | Early phase only | Any bounded process, full lifecycle |
Why this matters for anything you're tracking
If you've ever fit a trendline to a metric — signups, API calls, weekly active users — and extrapolated it forward, you did the same thing the early-2020 commentators did with case counts. The fit isn't wrong on the data you have; it's wrong about what the data will keep doing, because it silently assumes there's no K. Two fixes: either fit a logistic curve directly once you have enough data to estimate an inflection, or treat any exponential fit as valid only for forecasting a few periods past your last data point — the part of the S-curve where the brake term genuinely hasn't kicked in yet.
I worked through the full derivation — plus the R₀/epidemiology framing and the technology-adoption S-curve examples — over at the original post, if you want the longer version with more worked tables.
Top comments (0)