Alexander Bass
This note is one of many taken during 2026

On A Spigot Algorithm for the Digits of π (Rabinowitz and Wagon)

Two equivalent expressions of a standard decimal number:

1.234==1+210+3100+41000=1+110(2+110(3+110(4))) \begin{aligned} 1.234 &=\\ &= 1 + \frac{2}{10} + \frac{3}{100} + \frac{4}{1000}\\ &= 1 + \frac{1}{10}\Big(2 + \frac{1}{10}\Big(3 + \frac{1}{10}\Big(4\Big)\Big)\Big) \end{aligned}

A mixed-radix number is somewhat like having a different base for each digit. The primary objective of this paper is stating mixed-radix representation’s of ee and π\pi, and converting them to more useful bases.

Example of mixed-radix number:

a0+12(a1+13(a2+14(a3+))) a_0 + \frac{1}{2}\Big(a_1 + \frac{1}{3}\Big(a_2 + \frac{1}{4}\Big(a_3 + \ldots\Big)\Big)\Big)

Which expands to

a0+12a1+132a2+1432a3+ a_0 + \frac{1}{2}a_1 + \frac{1}{3 \cdot 2}a_2+ \frac{1}{4\cdot 3\cdot 2}a_3 + \ldots

When ai=1a_i=1 for all ii, this is the well-known power series for ee.

Euler’s number spigot

A. H. J. Sale doi 10.1093/comjnl/11.2.229.

Well-known ee power series:

e=n=01n! e = \sum_{n=0}^{\infty} \frac{1}{n!}

Given in mixed-radix form:

e=1+1(1+12(1+13(1+14(1+(1+1n))))) e = 1 + 1\Big(1 + \frac{1}{2}\Big(1+\frac{1}{3}\Big(1+\frac{1}{4}\Big(1+\ldots\Big(1+\frac{1}{n}\Big)\ldots\Big)\Big)\Big)\Big)

Also can be stated as a recursion:

rn=1+1n(rn+1) r_n = 1+ \frac{1}{n}(r_{n+1})

The paper then multiplies the fractional part of the expression by 1010\frac{10}{10}, and distributes the numerator into the recursion:

e=2+110[12(10+13(10+14(10+15(10+16(10+10())))))] e = 2 + \frac{1}{10} \left[ \frac{1}{2} \Big(10 + \frac{1}{3}\Big(10 + \frac{1}{4}\Big(10 + \frac{1}{5}\Big(10 + \frac{1}{6}\Big(10 + 10(\ldots)\Big)\Big)\Big)\Big)\Big) \right]

The goal of this manipulation is to extract base-10 digits from this mixed-radix form, one digit at a time. We first take this recursion to some specific depth. 55 in this example.

From right to left, we consecutively reduce the inner values mod the outer divisor, moving out the quotient in the process. An example is more clear:

16(10+)=106+6distribute=1+46+6divide and take remainder=1+16(4+)factor \begin{array}{llr} &\frac{1}{6}\left(10+\ldots\right)\\ &= \frac{10}{6} + \frac{\ldots}{6} &\text{distribute}\\ &= 1 + \frac{4}{6} + \frac{\ldots}{6} &\text{divide and take remainder}\\ &= 1 + \frac{1}{6}(4+\ldots) &\text{factor} \end{array}

This method of distributing the divisor, diving with remainder, and factoring is repeated:

e=2+110[7+12(0+13(1+14(0+15(1+16(4+10())))))] e=2+\frac{1}{10}\left[7+\frac{1}{2}\Big(0+\frac{1}{3}\Big(1+\frac{1}{4}\Big(0+\frac{1}{5}\Big(1+\frac{1}{6}\Big(4+10(\ldots)\Big)\Big)\Big)\Big)\Big)\right]

This shows clearly that the 2nd digit is 77. To get further digits, we again multiply the mixed-radix part by 10/1010/10.

e=2+710+1100[12(0+13(10+14(0+15(10+16(40+102())))))] e=2+\frac{7}{10}+ \frac{1}{100}\left[\frac{1}{2}\Big(0+\frac{1}{3}\Big(10+\frac{1}{4}\Big(0+\frac{1}{5}\Big(10+\frac{1}{6}\Big(40+10^2(\ldots)\Big)\Big)\Big)\Big)\Big)\right]

And repeat the process:

e=2+710+1100+1100[12(1+13(1+14(3+15(1+16(4+102())))))] \begin{aligned} e&=2+\frac{7}{10} + \frac{1}{100} \\ &+ \frac{1}{100}\left[\frac{1}{2}\Big(1+\frac{1}{3}\Big(1+\frac{1}{4}\Big(3+\frac{1}{5}\Big(1+\frac{1}{6}\Big(4+10^2(\ldots)\Big)\Big)\Big)\Big)\Big)\right] \end{aligned}

And again:

e=2+710+1102+8103+1103[12(0+13(0+14(1+15(1+16(4+103())))))] \begin{aligned} e&=2+\frac{7}{10} + \frac{1}{10^2} +\frac{8}{10^3}\\ &+ \frac{1}{10^3}\left[\frac{1}{2}\Big(0+\frac{1}{3}\Big(0+\frac{1}{4}\Big(1+\frac{1}{5}\Big(1+\frac{1}{6}\Big(4+10^3(\ldots)\Big)\Big)\Big)\Big)\Big)\right] \end{aligned}

And again:

e=2+710+1102+8103+0104+1104[12(0+13(0+14(1+15(1+16(4+104())))))] \begin{aligned} e&=2+\frac{7}{10} + \frac{1}{10^2}+ \frac{8}{10^3} + \frac{0}{10^4} \\ &+ \frac{1}{10^4}\left[\frac{1}{2}\Big(0+\frac{1}{3}\Big(0+\frac{1}{4}\Big(1+\frac{1}{5}\Big(1+\frac{1}{6}\Big(4+10^4(\ldots)\Big)\Big)\Big)\Big)\Big)\right] \end{aligned}

Which now produces an incorrect result.

e=2.7182e2.7180 \begin{aligned} e &= 2.7182\ldots\\ e &\ne 2.7180\ldots \end{aligned}

Why? Well, it is reasonable to assume that, because we limited the recursion to a finite number of levels, the remaining levels begin to become significant as the powers of 1010 rise.

Looking at the last reasonable result, we see at the end:

4+103() 4 + 10^3(\ldots)

Let’s call ()(\ldots) to be rr , we see

r=17(1+18()) r = \frac{1}{7}\Big(1 + \frac{1}{8}(\ldots)\Big)

Which expands to

r=17!+18!+ r = \frac{1}{7!} + \frac{1}{8!} + \ldots

Multiplying in the 10310^3 creates a value less than 11, and so it does not effect the calculations.

103r=103(17!+18!+)0.227 10^3r = 10^3\left(\frac{1}{7!} + \frac{1}{8!} + \ldots\right) \approx 0.227

However, when the power of ten is increased to 10410^4, we see:

104(17!+18!+)2.267 10^4\left(\frac{1}{7!} + \frac{1}{8!} + \ldots\right) \approx 2.267

Which is greater than one and will likely cause a deleterious effect when it is missing from the calculations. So, the question is: how many terms does the recursion need to be expanded to to obtain dd digits? Instead of thinking about the algorithm as algebraic manipulations on a recursive equation, I find it easier to think of as an array of numbers. The first step of the above example becomes:

i01234n23456v1010101010q74321r01014 \begin{array}{c:rrrrr} i & \small0 & \small1 & \small2 & \small3 & \small4 \\ n &\small 2 &\small 3 &\small 4 &\small 5 &\small 6 \\ \hline v &10 & 10 & 10 & 10 & 10 \\ q & 7 & 4 & 3 & 2 & 1 \\ r & 0 & 1 & 0 & 1 & 4 \\ \end{array}

Where:

iindexni=i+2divisorvivalueqi=(vi+qi+1)/niquotientri=(vi+qi+1)modniremainderq0=output digit \begin{array}{llll} i & & \text{index}\\ n_i &= i+2 & \text{divisor}\\ v_i & & \text{value}\\ q_i &= \lfloor (v_i+q_{i+1})/n_i \rfloor & \text{quotient}\\ r_i &= (v_i+q_{i+1}) \mod n_i &\text{remainder}\\ \hline\\ q_0 &= \text{output digit} \end{array}

Inside the array, there are 5 slots (columns). Each slot represents a level of recursion. More slots => more digits extractable.

Let dd represent the number of digits being extracted, and ss represent the number of slots. A calculation using ss slots will yield dd digits when the following is true:

1>10d(x=s+21x!) 1 > 10^d\left(\sum_{x=s+2}^{\infty} \frac{1}{x!} \right)

Infinite sums are cumbersome so instead a simpler form is desired. So long as the value of the sum overestimated, a simpler form will still work.

The following form is valid according to Desmos and WolframAlpha, so I’ll trust those sites on that.

1(s+1)!>x=s+21x! \frac{1}{(s+1)!} \gt \sum_{x=s+2}^{\infty} \frac{1}{x!}

Substituting this simpler expression in:

(s+1)!>10d (s+1)! > 10^d

Solve for digits:

ln((s+1)!)ln(10)>d \frac{\ln((s+1)!)}{\ln(10)} > d

Plotting this, we see

The linear approximation of

ds3 d \le s-3

Fits well under the entire domain. This approximation becomes poorer as more slots are added, but never overestimates. When s=70s=70, d100d \le \approx100

A polynomial or piecewise approximation could provide improvement. The original paper provides a different approximation entirely. I have not reviewed that approximation.

When in one of the previous examples the result of 2.71802.7180\ldots was obtained, only 5 slots were used, and so only two digits after the decimal place should have been expected with confidence to be correct.


Back again to thinking of the problem algorithmically:

i01234n23456v1010101010q74321r01014 \begin{array}{c:rrrrr} i & \small0 & \small1 & \small2 & \small3 & \small4 \\ n &\small 2 &\small 3 &\small 4 &\small 5 &\small 6 \\ \hline v &10 & 10 & 10 & 10 & 10 \\ q & 7 & 4 & 3 & 2 & 1 \\ r & 0 & 1 & 0 & 1 & 4 \\ \end{array}

Where:

iindexni=i+2divisorvivalueqi=(vi+qi+1)/niquotientri=(vi+qi+1)modniremainderq0=nth digit of e \begin{array}{llll} i & & \text{index}\\ n_i &= i+2 & \text{divisor}\\ v_i & & \text{value}\\ q_i &= \lfloor (v_i+q_{i+1})/n_i \rfloor & \text{quotient}\\ r_i &= (v_i+q_{i+1}) \mod n_i &\text{remainder}\\ \hline\\ q_0 &= n^{\text{th}} \text{ digit of } e\\ \end{array}

To find the second digit, update the table such that:

Then apply the same rules, stopping after i=1i=1:

i01234n23456v01001040q13036r01314 \begin{array}{c:rrrrr} i & \small0 & \small1 & \small2 & \small3 & \small4 \\ n &\small 2 &\small 3 &\small 4 &\small 5 &\small 6 \\ \hline v & 0 & 10 & 0 & 10 & 40 \\ q & 1 & 3 & 0 & 3 & 6 \\ r & 0 & 1 & 3 & 1 & 4 \\ \end{array}

And so digit #2 is 11: 2.712.71\ldots.

An implementation in Rust is provided.

View Implementation
fn main() {
    for i in 1..20 {
        println!("{:?}", e(i));
    }
}

fn e(d: u32) -> Vec<u8> {
    let mut digits = vec![0; d as usize];
    let slots = d as usize + 3;
    let mut tmp = vec![10; slots];
    for stop in 0..d as usize {
        let mut carry = 0;
        for i in (0..slots).rev() {
            let n = i as u32 + 2;
            let s = carry + tmp[i];
            let q = s / n;
            let r = s % n;
            tmp[i] = r * 10;
            carry = q;
        }
        digits[stop] = carry as u8;
    }
    digits
}

This implementation seems very similar to the Algol 60 one provided in the paper. I’m not familiar with the array syntax and semantics in Algol 60, so there may be variation there.

Back to π

After a slight detour, the study of π is resumed.

Todo….

This note is one of many taken during 2026