# Continued Fractions and Function “rat” 1

Posted by **Cleve Moler**,

Let me tell you about MATLAB's controversial function `rat`.

### Contents

#### Classic `rat`

The function `rat` is older than MathWorks. It was one of the 73 functions in my original Fortran MATLAB. Here is the help entry from 40 years ago.

```
<>
help rat
```

RAT An experimental function which attempts to remove the roundoff error from results that should be "simple" rational numbers. RAT(X) approximates each element of X by a continued fraction of the form

a/b = d1 + 1/(d2 + 1/(d3 + ... + 1/dk))

with k <= len, integer di and abs(di) <= max . The default values of the parameters are len = 5 and max = 100. RAT(len,max) changes the default values. Increasing either len or max increases the number of possible fractions. <A,B> = RAT(X) produces integer matrices A and B so that

A ./ B = RAT(X)

Some examples:

long T = hilb(6), X = inv(T) <A,B> = rat(X) H = A ./ B, S = inv(H)

short e d = 1:8, e = ones(d), A = abs(d'*e - e'*d) X = inv(A) rat(X) display(ans)

#### An example

Let's try that second example with modern MATLAB and `format rat`.

format short d = 1:6 e = ones(size(d)), A = abs(d'*e - e'*d) format short e X = inv(A) format rat X

d = 1 2 3 4 5 6 e = 1 1 1 1 1 1 A = 0 1 2 3 4 5 1 0 1 2 3 4 2 1 0 1 2 3 3 2 1 0 1 2 4 3 2 1 0 1 5 4 3 2 1 0 X = -4.0000e-01 5.0000e-01 8.3267e-17 -3.3307e-17 -9.9920e-17 1.0000e-01 5.0000e-01 -1.0000e+00 5.0000e-01 3.6978e-33 8.8818e-17 -5.5511e-17 8.3267e-17 5.0000e-01 -1.0000e+00 5.0000e-01 1.4063e-16 -9.2519e-17 -3.3307e-17 3.6978e-33 5.0000e-01 -1.0000e+00 5.0000e-01 5.5511e-17 -9.9920e-17 8.8818e-17 1.4063e-16 5.0000e-01 -1.0000e+00 5.0000e-01 1.0000e-01 -5.5511e-17 -9.2519e-17 5.5511e-17 5.0000e-01 -4.0000e-01 X = Columns 1 through 5 -2/5 1/2 * * * 1/2 -1 1/2 * * * 1/2 -1 1/2 * * * 1/2 -1 1/2 * * * 1/2 -1 1/10 * * * 1/2 Column 6 1/10 * * * 1/2 -2/5

Which version of `X` do you prefer? The first, printed with a floating point format so the roundoff errors are readily apparent. Or the second, printed with a rational format that seeks to disguise the roundoff. For this example, almost everybody would vote for the rational format.

#### Don't try to hide it

But I have learned that it is better to explain floating point arithmetic than it is to try to hide it. Besides, most important MATLAB calculations involve larger matrices, and eigenvalues, differential equations and signal processing. They don't have such tidy results.

So, we don't have much use for `rat` and `format rat` today, but they're still there and, as I said, they're moderately controversial.

#### The controversial algorithm

The core of `rat` generates continued fractions by repeatedly subtracting off the integer part and taking the reciprocal of what is left. If there is nothing left to reciprocate, the process terminates because the input is a rational number. If the input is irrational, the process does not terminate.

The controversary comes from what is meant by integer part. To generate proper continued fractions, integer part should be `floor`. That always leaves a positive fraction to reciprocate. But 40 years ago, I got clever and used `round` instead of `floor`. That means it may take fewer terms to obtain a specified accuracy, but the continued fractions are, shall I say, *unorthodox*. Let's see more examples.

#### pi

Let's generate increasingly accurate rational approximations to $\pi$. With two output arguments, `rat` unwinds the continued fraction to produce two integers whose ratio has the same value. The accuracy of `rat` is determined by an optional tolerance, `rat(x,tol)`. The default tolerance is `1.0e-6*norm(x)`.

Requesting low accuracy produces a familiar approximation of $\pi$.

```
form = ' tol = %6.1e \n r = %s \n = %d/%d\n';
tol = 1.0e-2;
r = rat(pi,tol);
[n,d] = rat(pi,tol);
fprintf(form,tol,r,n,d)
```

tol = 1.0e-02 r = 3 + 1/(7) = 22/7

The default tolerance produces a less familiar, but elegant approximation.

tol = 1.0e-6; r = rat(pi,tol); [n,d] = rat(pi,tol); fprintf(form,tol,r,n,d)

tol = 1.0e-06 r = 3 + 1/(7 + 1/(16)) = 355/113

Somewhat higher accuracy produces a negative term.

tol = 1.0e-8; r = rat(pi,tol); [n,d] = rat(pi,tol); fprintf(form,tol,r,n,d)

tol = 1.0e-08 r = 3 + 1/(7 + 1/(16 + 1/(-294))) = 104348/33215

Another negative term.

tol = 1.0e-11; r = rat(pi,tol); [n,d] = rat(pi,tol); fprintf(form,tol,r,n,d)

tol = 1.0e-11 r = 3 + 1/(7 + 1/(16 + 1/(-294 + 1/(3 + 1/(-4))))) = 1146408/364913

#### Latex

If we're careful with the curly braces, Latex and MathJax can typeset that last continued fraction and produce a graphic for this post.

{{1}\over{{7+ {{1}\over{{16+ {{1}\over{{-294+ {{1}\over{{3+ {{1}\over{-4}}}}}}}}}}}}}}

$${{1}\over{{7+ {{1}\over{{16+ {{1}\over{{-294+ {{1}\over{{3+ {{1}\over{-4}}}}}}}}}}}}}}$$

#### ratp

I have another function, `ratp`, that is `rat` with `round` replaced by `floor` so that it produces proper, but longer, continued fractions with positive terms. Let's see how it compares to `rat`. Since the MATLAB quantity `pi` that approximates $\pi$ is rational, we can set `tol` to zero without fear of an infinite loop.

form = ' tol = %6.1e \n r = %s \n p = %s \n\n'; for tol = 10.^[-2 -6 -8 -11 -inf] fprintf(form,tol,rat(pi,tol),ratp(pi,tol)) end

tol = 1.0e-02 r = 3 + 1/(7) p = 3 + 1/(7) tol = 1.0e-06 r = 3 + 1/(7 + 1/(16)) p = 3 + 1/(7 + 1/(15 + 1/(1))) tol = 1.0e-08 r = 3 + 1/(7 + 1/(16 + 1/(-294))) p = 3 + 1/(7 + 1/(15 + 1/(1 + 1/(292)))) tol = 1.0e-11 r = 3 + 1/(7 + 1/(16 + 1/(-294 + 1/(3 + 1/(-4))))) p = 3 + 1/(7 + 1/(15 + 1/(1 + 1/(292 + 1/(1 + 1/(1 + 1/(1 + 1/(2)))))))) tol = 0.0e+00 r = 3 + 1/(7 + 1/(16 + 1/(-294 + 1/(3 + 1/(-4 + 1/(5 + 1/(-15))))))) p = 3 + 1/(7 + 1/(15 + 1/(1 + 1/(292 + 1/(1 + 1/(1 + 1/(1 + 1/(2 + 1/(1 + 1/(3 + 1/(1 + 1/(14))))))))))))

#### phi

The world's most irrational number, as measured by the length of continued fraction approximations, is $\phi$, the golden ratio. (Proof: solve $x = 1 + 1/x$.) The coefficients in the proper continued fraction are all ones, thereby achieving the slowest possible convergence. For the same accuracy, `ratp(phi,tol)` requires about twice as many terms as `rat(phi,tol)`.

```
format short
phi = (1 + sqrt(5))/2
tol = 1.0e-4
r = rat(phi)
p = ratp(phi)
```

phi = 1.6180 tol = 1.0000e-04 r = '2 + 1/(-3 + 1/(3 + 1/(-3 + 1/(3 + 1/(-3 + 1/(3 + 1/(-3)))))))' p = '1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1))))))))))))))'

tol = 0; lengthr = length(rat(phi,tol)) lengthp = length(ratp(phi,tol))

lengthr = 154 lengthp = 297

#### DIY

(For my international readers, DIY stands for "Do It Yourself".) Create your own `ratp` by making a one-word edit in a copy of `toolbox\matlab\specfun\rat.m`. Compare the two functions on `sqrt(2)`. Warning: all is not what appears at first glance.

Get the MATLAB code

Published with MATLAB® R2018b

**Category:**- Fun,
- History,
- Numerical Analysis,
- Precision

## 1 CommentsOldest to Newest

**1**of 1

## Recent Comments