Computing π and e: Three Classical Algorithms Each
This chapter presents two programs that compute π and e. Each constant uses three different algorithms. The point is not to compute π or e — one line of Prelude does that. The point is how six classical algorithms behave differently. One needs ten million terms for six digits. One fails due to floating-point rounding. One squeezes the answer between polygons. One algorithm shows why e is irrational.
The programs live in source-code/maths. Build and run with:
1 cd source-code/maths
2 cabal run pi # three methods for π
3 cabal run e # three methods for e
Dear reader, we first cover the math, then list the programs at the end of the chapter.
π: Three Methods
2.1 Madhava-Leibniz: Two Lines, Ten Million Terms
The series
1 π/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - ...
gives

Everything is elementary: a geometric series, integrated term by term, evaluated at x = 1.
The trouble is that x = 1 is the boundary of convergence. The terms 1/(2k+1) shrink like 1/k. The truncation error after n terms is about 1/n:
1 after n terms: error ≈ 1/n
Measured:
| budget | abs error |
|---|---|
| 1,000,000 | 1.000e-6 |
| 10,000,000 | 1.000e-7 |
Ten digits need ten billion terms.
This series converges only conditionally. Group terms and you can make it sum to anything — Riemann’s rearrangement theorem. Madhava found it around 1400, two and a half centuries before Leibniz.
2.2 Machin: 100 Digits by Hand
1 π = 16·arctan(1/5) - 4·arctan(1/239)
John Machin published this in 1706 and used it to compute 100 digits by hand.
Where does it come from? Multiply complex numbers:
1 (5+i)⁴ · (239-i) = 114244·(1+i)
The argument of 1+i is 45° = π/4. Arguments add under multiplication, so:
1 4·arctan(1/5) - arctan(1/239) = π/4
Multiply by 4. That is the entire proof.
The magic is small arguments. The arctangent series converges quickly when x is small. Successive terms shrink by x²:
- arctan(1/5): terms shrink by 1/25 → about 1.4 digits per term
- arctan(1/239): terms shrink by 1/57121 → about 4.8 digits per term
Measured:
| terms | abs error |
|---|---|
| 4 | 8.814e-7 |
| 10 | 8.882e-16 |
| 20 | 8.882e-16 |
Each term multiplies error by about 25 until Double precision ends.
2.3 Archimedes: The Squeeze, 2000 Years Before Calculus
Around 250 BC Archimedes trapped π between inscribed and circumscribed polygons:
1 n·sin(π/n) < π
2 n·tan(π/n) > π
Starting from a hexagon (6 sides) and doubling repeatedly, he concluded:
1 223/71 < π < 22/7
The error falls off quadratically because

Every doubling divides error by 4.
The textbook recurrence is numerically dead. Written naively:
1 s_{2n} = sqrt(2 - sqrt(4 - s^2))
As s shrinks, the inner square root rounds to nearly 2. Subtracting two nearly-equal numbers throws away digits. By 26 doublings the tangent rounds to 0 and the result becomes NaT.
The fix is rationalising the numerator:

And then the method runs out of bits. At 25 doublings the error is below Double spacing (4.4e-16). More doublings give identical results.
e: Three Methods
3.1 Maclaurin: Factorials, and Why e Is Irrational
1 e = Σ 1/k! = 1 + 1 + 1/2 + 1/6 + 1/24 + ...
Convergence is factorial. Term k is 1/k!, so the ratio of consecutive terms is 1/k which improves as you go. Eighteen terms exhaust Double precision.
This is also a proof that e is irrational. Suppose e = p/q. Multiply by q!:
1 q!·e = (q-1)!·p ← an integer
2
3 q!·e = Σ_{k≤q} q!/k! + Σ_{k>q} q!/k!
4 integer strictly between 0 and 1
An integer that is not an integer: e cannot be a fraction.
Do not compute factorials. Double overflows at 171!. The code divides repeatedly: 1/k! = (1/(k-1)!)/k.
3.2 Compound Interest: Bernoulli’s Limit, Richardson’s Rescue
Jacob Bernoulli (1683) discovered e from compound interest:

As a computational method it is terrible. The error is only e/(2n) — first order. Even n = 10⁶ leaves the sixth decimal wrong.
Richardson extrapolation cancels the error. The expansion
1 L(n) = e·(1 - 1/(2n) + 11/(24n²) - ...)
shows that 2·L(2n) - L(n) cancels the 1/n term, leaving error of order 1/n².
| n | raw error | after Richardson |
|---|---|---|
| 10³ | 1.359e-3 | 6.220e-7 |
| 10⁸ | 1.359e-8 | 4.441e-16 |
The floating-point trap. Computing (1 + 1/n)^n for large n:
1 log(1 + 1/n) rounds 1/n, then multiplies by n, amplifying the error.
At n = 10¹² the naive computation is off by eight orders of magnitude with wrong sign.
The code uses log1p computed via 2·atanh(x/(2+x)).
3.3 Euler’s Continued Fraction: The Pattern e Has
Euler proved:
1 e = [2; 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8, ...]
The pattern: a₀ = 2, aₖ = 2(k+1)/3 when k ≡ 2 (mod 3), otherwise 1. Almost no number has such a pattern.
Convergents alternate above and below e, closing in quickly. Twenty-one convergents give exact Double precision.
Contrast π. Its continued fraction has no pattern:
1 π = [3; 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, ...]
The large term 292 generates 103993/33102 — nine correct decimals from five digits. The convergent before it is 355/113 — six correct decimals from three digits.
Code Structure
Both programs follow the same pattern:
- Three algorithm functions
- Data type for methods with metadata
- Command-line interface
- Timing and reporting
1 maths/
2 ├── pi.hs π: 3 methods, CLI, reporting
3 ├── e.hs e: 3 methods, CLI, reporting
4 ├── maths.cabal two executables
5 └── README.md detailed discussion
Each program is one file. No shared modules. For two files this is simpler than a module split.
Running the Methods
| Program | Method | Budget Meaning | Default |
|---|---|---|---|
| pi | leibniz | terms | 1,000,000 |
| pi | machin | terms each arctangent | 20 |
| pi | archimedes | doublings (n = 6·2ᵏ) | 25 |
| e | taylor | terms of Σ1/k! | 20 |
| e | limit | compounding periods n | 100,000,000 |
| e | continued-fraction | convergents | 30 |
Options:
-m NAME— run one method-n N— set budget-j— JSON output-v— verbose mode--list— describe methods
Source Code Listings
The complete source code for both programs is in the source-code/maths directory. Each file is self-contained with all algorithm implementations, command-line handling, and output formatting.
pi.hs
The program estimates π using three classical methods: Madhava-Leibniz series, Machin’s arctangent formula, and Archimedes’ polygon method.
See source-code/maths/pi.hs for the complete implementation.
e.hs
The program estimates Euler’s number e using three methods: Maclaurin series, compound-interest limit with Richardson extrapolation, and Euler’s continued fraction.
See source-code/maths/e.hs for the complete implementation.
π Program (pi.hs)
The complete pi.hs implementation estimates π using three classical algorithms. The program is self-contained with all algorithm implementations, command-line interface, and reporting logic in a single file.
Key algorithm: Madhava-Leibniz Series
The leibnizPi function computes π using the alternating series. Each term is added to an accumulator, keeping the computation in constant space.
1 leibnizPi :: Int -> Double
2 leibnizPi terms
3 | terms <= 0 = 0
4 | otherwise = 4 * go 0 0 1
5 where
6 go :: Double -> Int -> Double -> Double
7 go acc k denominator
8 | k >= terms = acc
9 | otherwise = go (acc + sign / denominator) (k + 1) (denominator + 2)
10 where
11 sign = if even k then 1 else -1
Key algorithm: Machin’s Formula
The machinPi function uses Machin’s arctangent identity with Taylor series evaluation.
1 machinPi :: Int -> Double
2 machinPi terms =
3 16 * arctanSeries (1 / 5) terms - 4 * arctanSeries (1 / 239) terms
Key algorithm: Archimedes’ Method
The archimedesPi function bounds π using inscribed and circumscribed polygons, with numerically stable recurrences.
1 archimedesBounds :: Int -> (Double, Double)
2 archimedesBounds doublings = go 6 1 (2 / sqrt 3) (max 0 doublings)
3 where
4 go :: Double -> Double -> Double -> Int -> (Double, Double)
5 go sides inscribed circumscribed remaining
6 | remaining <= 0 = (sides * inscribed / 2, sides * circumscribed / 2)
7 | otherwise = go (2 * sides) inscribed' circumscribed' (remaining - 1)
8 where
9 inscribed' = inscribed / sqrt (2 + sqrt (4 - inscribed * inscribed))
10 circumscribed' = circumscribed / (1 + sqrt (1 + circumscribed * circumscribed / 4))
Full implementation: See source-code/maths/pi.hs for the complete file including imports, command-line handling, and output formatting.
e Program (e.hs)
The complete e.hs implementation estimates Euler’s number e using three classical algorithms. The program uses exact rational arithmetic for the continued fraction method.
Key algorithm: Maclaurin Series
The eTaylor function computes e using the series Σ 1/k!, building each term by division to avoid factorial overflow.
1 eTaylor :: Int -> Double
2 eTaylor terms
3 | terms <= 0 = 0
4 | otherwise = go 1 1 1
5 where
6 go :: Double -> Double -> Int -> Double
7 go acc term k
8 | k >= terms = acc
9 | otherwise = go (acc + next) next (k + 1)
10 where
11 next = term / fromIntegral k
Key algorithm: Compound Interest Limit with Richardson Extrapolation
The eLimitRichardson function accelerates the convergence of the naive limit using Richardson extrapolation.
1 eLimitRichardson :: Int -> Double
2 eLimitRichardson n
3 | n <= 0 = 0
4 | otherwise = 2 * eLimit (2 * n) - eLimit n
Key algorithm: Euler’s Continued Fraction
The eContinuedFraction function computes e using Euler’s continued fraction with exact rational arithmetic.
1 eContinuedFractionCoef :: Int -> Integer
2 eContinuedFractionCoef 0 = 2
3 eContinuedFractionCoef k
4 | k `mod` 3 == 2 = 2 * fromIntegral ((k + 1) `div` 3)
5 | otherwise = 1
Full implementation: See source-code/maths/e.hs for the complete file including imports, command-line handling, and output formatting.
Running the Code
To build and run the programs:
1 $ cd source-code/maths
2 cabal build
3 $ make run-pi
4 $ cabal run pi
5 reference value : 3.141592653589793
6
7 method budget estimate abs error time
8 ------------------ ------------ ------------------- ----------- ----------
9 leibniz 1,000,000 3.141591653589774 1.000e-6 0.008249s
10 machin 20 3.141592653589794 8.882e-16 0.000001s
11 archimedes 25 3.141592653589795 1.776e-15 0.000003s
12
13 $ cabal run-e
14
15 method budget estimate abs error time
16 ------------------ ------------ ------------------- ----------- ----------
17 taylor 20 2.718281828459046 4.441e-16 0.000000s
18 limit 100,000,000 2.718281828459045 4.441e-16 0.000039s
19 continued-fraction 30 2.718281828459045 0.000e0 0.000241s
For verbose output with method descriptions:
1 cabal run pi -- --verbose
2 cabal run e -- --list
Lessons
Error analysis matters. The Madhava-Leibniz series is simple but slow. Machin’s formula is complex but fast.
Numerical stability matters. Archimedes’ naive recurrence fails at 26 doublings. Rationalising helps.
Exact arithmetic beats floating-point. The continued fraction uses
Rationaluntil the final conversion.Convergence rates differ. Leibniz: 1/n. Machin: geometric. Archimedes: 1/n². Taylor: factorial. Continued fraction: each convergent is the best possible approximation.
Algorithms have stories. Madhava (1400s), Machin (1706), Archimedes (250 BC), Bernoulli (1683), Euler (1737). Each represents a different era of mathematics.