Most compound interest calculators use a loop: multiply by (1 + r) for each period. That works fine for 30 years of annual compounding — 30 iterations, no problem. But try 30 years of daily compounding (10,950 periods) or a mortgage with monthly payments over 40 years (480 periods with amortization) and you start feeling the slowdown. Now multiply that by a Monte Carlo simulation running 10,000 scenarios and suddenly your browser tab freezes.
The Math
The naive approach to calculating (1 + r)^n:
result = 1
for i in range(n):
result *= (1 + r)
That's O(n). For n = 10,950, that's 10,950 multiplications.
Exponentiation by squaring (also called binary exponentiation or fast power) reduces this to O(log n):
function fastPow(base, exp):
result = 1
while exp > 0:
if exp is odd:
result = result * base
base = base * base
exp = exp >> 1 # integer division by 2
return result
For n = 10,950 (binary: 10101011000110), that's about 14 multiplications instead of 10,950. That's roughly a 780x speedup.
Why This Actually Matters
People don't notice 10,950 multiplications — modern JavaScript handles that in microseconds. The real bottleneck happens when you combine operations:
- Monte Carlo simulations — 10,000 scenarios × 30 years × 12 months × exponentiation = hundreds of millions of operations
- Real-time slider updates — every drag recalculates the full projection. At 60fps with debouncing, you need <16ms per frame
- Mobile browsers — older phones have slower JS engines. What's instant on desktop can be sluggish on a $200 Android
Here's the performance difference measured on a compound interest calculator handling daily compounding over 40 years, averaged over 100,000 runs:
| Method | Time (100k runs) | Multiplications per call |
|---|---|---|
| Loop (O(n)) | 847ms | 14,600 |
| Fast pow (O(log n)) | 1.2ms | ~14 |
| Speedup | 706x | — |
Implementation Notes
A few footguns I ran into:
1. Negative exponents — The algorithm above handles positive integer exponents. For negative exponents (discounting), compute fastPow(base, -exp) and return 1/result. For fractional exponents (continuous compounding), use Math.exp() instead — don't try to adapt this algorithm.
2. Integer overflow on exp >> 1 — JavaScript bitwise operators work on 32-bit signed integers. If your exponent exceeds 2^31 - 1 (which for financial calculations it won't, but worth knowing), use Math.floor(exp / 2) instead.
3. Floating-point accumulation — O(log n) multiplications means less floating-point error accumulation. This is a nice side benefit — your final values will be slightly more accurate with the fast method.
The algorithm is simple enough to implement in any language in under 10 lines, and it's the kind of optimization that separates a sluggish calculator from a snappy one. If you're building any financial tool that does exponentiation in a hot path, this is probably the highest-ROI optimization you can make.
This post is part of a series on the algorithms behind financial calculators. No frameworks, no libraries — just vanilla JavaScript and math.
Top comments (0)