Continued Fraction Streams

Every real number can be represented as a potentially infinite stream of digits and also as a potentially infinite stream of continued fraction coefficients, for example:

(The final expression uses a less cool-looking but probably more practical notation for continued fractions.)

I wrote some Python code which converts streams of digits to streams of continued fraction coefficients, and vice-versa.

This writeup shows some example uses of the software, and discusses some of the nuances of converting between these infinite streams.

For Example

Next time you forget the digits of the golden ratio, just remember that its continued fraction coefficients are all ones:

…then grab an endless stream of ones from the unexpectedly useful yes command:

> yes 1

…and pipe them into the coefficients-to-digits script:

> yes 1 | python3 as_digits.py

This will output a never-ending stream of the digits of .

To check that things are working, you can even pipe the digits back into the digits-to-coefficients script:

> yes 1 | python3 as_digits.py | python3 continued_digits.py

Cool it works!

Lochs’ Theorem

One of the countless interesting things about the golden ratio is that it’s the “most irrational” number.

One way to glimpse this is while converting its stream of continued fraction coefficients to digits—it takes many coefficients to get each digit:

Lochs’ theorem says that for almost all numbers, it takes 0.97 continued fraction coefficients on average to determine each decimal digit. But for the golden ratio it takes 2.39 coefficients to determine each decimal digit, the worst case1 possible.

We can see both of these behaviors by comparing to a few random streams of digits. It takes about 970 coefficients to get 1,000 digits for random numbers, but it takes 2,395 coefficients to get 1,000 digits of :

Number of coefficients versus number of decimals, for phi and three random numbers

A Problem

Let’s say we’ve received the digits from our input stream of digits. If we just truncate and compute2 the continued fraction we get:

So maybe we can safely emit these coefficients into our output stream and then keep emitting whatever coefficients come next?

Let’s say we receive the digit next, so far giving us

The first three coefficients are the same as what we’ve already outputted, but the fourth coefficient has changed from to !

In most streaming situations there’s no way to undo or change something we’ve already outputted—and we can’t wait until the stream ends, because it might be infinite—so we’re doomed.

A Solution

We should emit a coefficient only once we’re sure it will never change.

We can do this by seeing what would happen if we receive the smallest or largest digits from the stream in the future, giving us bounds on what could possibly happen.

For example, if we’ve so far received then the smallest number it could possibly be is if we receive zeroes forever, , and the largest number it could be is if we receive nines forever, So we can bound the true number with:

Of course, (equivalent to the stream simply ending) and , both of which have finite continued fractions that we can compute:

Both bounds have in common, so across the entire range of possible values, no matter what the future digits in the stream are, these coefficients will remain the same, and we can safely output them:

Here’s a more thorough example of what a program would do, processing one digit at a time, and outputting coefficients as soon as they’re certain:

In the other direction

What if we want to go in the other direction, from streams of continued fraction coefficients to streams of digits?

The two biggest changes that can happen to the number are if there are no more coefficients, or if there is one more coefficient and it is a .

If that sounds confusing, remember that all coefficients must be positive integers, and ponder some examples:

(Notice that the inequalities alternate back-and-forth. Continued fractions oscillate around their limit—unlike digit representations, which increase toward their limit.)

Using these bounds, we can convert the stream of coefficients to a stream of digits, emitting whatever digits remain unchanged across both bounds:


Generalizing the previous examples, we can always bound a stream of continued fraction coefficients3 as:

And we get an almost identical inequality for digit streams:

  1. is also the “worst case” regarding Khinchin’s constant, since the geometric mean of its coefficients is always 1, the smallest possible.

  2. Here’s how to compute the continued fraction :

  3. What happens to the bounds if we know the stream of continued fraction coefficients is infinite? (This is equivalent to knowing that the number is irrational.)

    We can get arbitrarily close to the stream “ending” if the next coefficient is arbitrarily large. And for the other bound, we can get a followed by an arbitrarily large coefficient.

    So all that happens is that the inequality becomes strict, i.e. it is no longer possible to actually reach the bounds:

    (Another way to notice this is that the bounds are rational, so of course our irrational number can’t equal them.)

    With digits, on the other hand, nothing changes even if we know the stream is infinite. We can still reach both bounds, with an endless stream of zeroes or an endless stream of nines.