The `MASS`

package contains two small helper functions called `fractions`

and `rational`

. Originally these were included to allow patterns in patterned matrices to be more easily exhibited and hence, detected. This happens when the entries are expressible in the form of “vulgar” fractions, i.e. numerator/denominator, rather than as recurring decimals.

For example a Hilbert matrix:

C1 | C2 | C3 | C4 | C5 | |
---|---|---|---|---|---|

R1 | 1.0000000 | 0.5000000 | 0.3333333 | 0.2500000 | 0.2000000 |

R2 | 0.5000000 | 0.3333333 | 0.2500000 | 0.2000000 | 0.1666667 |

R3 | 0.3333333 | 0.2500000 | 0.2000000 | 0.1666667 | 0.1428571 |

R4 | 0.2500000 | 0.2000000 | 0.1666667 | 0.1428571 | 0.1250000 |

R5 | 0.2000000 | 0.1666667 | 0.1428571 | 0.1250000 | 0.1111111 |

has a pattern much easier to appreciate if the entries are expressed as vulgar fractions:

C1 | C2 | C3 | C4 | C5 | |
---|---|---|---|---|---|

R1 | 1 | 1/2 | 1/3 | 1/4 | 1/5 |

R2 | 1/2 | 1/3 | 1/4 | 1/5 | 1/6 |

R3 | 1/3 | 1/4 | 1/5 | 1/6 | 1/7 |

R4 | 1/4 | 1/5 | 1/6 | 1/7 | 1/8 |

R5 | 1/5 | 1/6 | 1/7 | 1/8 | 1/9 |

This package facilitates the calculation and presentation of numerical quantities as rational approximations.

To be more specific, a rational approximation to a real number is a ratio of two ** integers**, the

A second vignette, *Theory*, outlines, for the curious reader, the standard method used to find them, using continued fractions, and some basic mathematical properties. It is of mathematical interest, but not necessary to use the package itself.

This small package offers merely a way to present numerical values by means of close rational approximations, displayed as vulgar fractions. It does *not* provide exact rational arithmetic. Such a facility would require unlimited precision rational arithmetic, which is well beyond the scope of this package. In fact, integer overflow is a constant limitation in our use of fractions in the present package.

The package provides two main functions: ** fractional** and

`numerical`

Readers interested in the details of the algorithm used and its implementation in `R`

should refer to the *Theory* vignette.

`fractional`

and `unfractional`

`fractional`

is the main function of the package. The argument list is as follows:

`function (x, eps = 1e-06, maxConv = 20, sync = FALSE) `

A numeric vector, matrix or array.`x`

An absolute error tolerance. The rational approximation is the first convergent for which the absolute error falls below this bound.`eps = 1e-06`

A secondary stopping criterion. An upper limit on the number of convergents (See`maxConv = 20`

*Theory*vignette) to consider.

Should the numerical value be “synchronized” with the displayed numerical approximation? See below.`sync = FALSE`

The value returned is a numeric vector like `x`

, but inheriting from class `fractional`

. It may be used in arithmetic operations in the normal way and retains full floating point accuracy, unless `sync = TRUE`

. When printed, or coerced to character, however, the rational approximation is used as the representation, which as a numerical value may not precisely equal the full floating point value.

If `sync = TRUE`

is set, then the numerical value is changed to agree with the numerical value and this agreement is maintained in synchrony, as much as possible with floating point arithmetic, during future numerical operations.

`unfractional`

The function ** unfractional** is a convenience function that strips the class and some additional attributes from an object of class

`"fractional"`

, thus demoting it back to a numerical object like the one from which it was generated.`fractional`

objects in arithmetic operations.Methods are provided for the `S3`

group generic functions `Ops`

and `Math`

, allowing the mathematical operators and simple mathematical functions to be used with them. The normal rules apply. In an expression `e1 + e2`

(where `+`

can be any of the operators) the `fractional`

method is dispatched if *either* `e1`

*or* `e2`

*(or both)* is/are of class `fractional`

, and the result is of class `fractional`

.

For the `Math`

group generic, operations are conducted but treating the objects as a normal numeric object, that is, ignoring the `fractional`

class.

`as.character`

The package provides a method for the primitive generic function `base::as.character`

for objects of class `"fractional"`

. The arguments are as follows:

`function (x, eps = attr(x, "eps"), maxConv = attr(x, "maxConv"), ...)`

That is, the values for `eps`

and `maxConv`

are carried forward from the original call. This is because it is only when the object is displayed or coerced to character that the computation of the continued fraction is carried out.

The result is a character string object of primary class `charFrac`

. This primary class membership essentially ensures that the object is printed without quotation marks.

`numerators`

and `denominators`

These two `S3`

generic functions will extract the numerators and denominators, respectively.

The argument is a single object, `x`

, and the result is an object of type `integer`

, but carrying attributes such as `dim`

or `dimnames`

from the original.

Methods are provided for objects of `S3`

class `fractional`

or `charFrac`

`numerical`

This is essentially a method function for `base::as.numeric`

providing coercion to numeric for objects of `S3`

class `fractional`

or `charFrac`

.

It is written as an S3 generic function, and the main methods uses `numerators`

and `denominators`

in an obvious way.

The `default`

method passes the object to `base::as.numeric`

.

`rat`

, `ratr`

and `.ratr`

The function `rat`

is a front-end to the `C++`

function that carries out the continued fraction computations. It would normally not be called directly by users.

The functions `ratr`

and `.ratr`

, in combination, provide the identical computation but done in pure `R`

. They are provided for pedagogical and timing purposes only.

`vfractional`

This is a variant on `fractional`

, without the `sync`

argument, but *vectorized* with respect to the three arguments `x`

, `eps`

and `maxConv`

. It is mainly used for examples.

The *Theory* vignette provides many example in context, but the following very elementary examples may give the flavour of how the functions are used.

```
library(dplyr)
(x <- matrix(1:9/12, 3, 3) %>% fractional)
```

```
[,1] [,2] [,3]
[1,] 1/12 1/3 7/12
[2,] 1/6 5/12 2/3
[3,] 1/4 1/2 3/4
```

`(xr <- 1/x)`

```
[,1] [,2] [,3]
[1,] 12 3 12/7
[2,] 6 12/5 3/2
[3,] 4 2 4/3
```

`x + 10`

```
[,1] [,2] [,3]
[1,] 121/12 31/3 127/12
[2,] 61/6 125/12 32/3
[3,] 41/4 21/2 43/4
```

`(x2 <- x^2)`

```
[,1] [,2] [,3]
[1,] 1/144 1/9 49/144
[2,] 1/36 25/144 4/9
[3,] 1/16 1/4 9/16
```

`(sx2 <- sqrt(x2)) ## demoted back to numeric by sqrt()`

```
[,1] [,2] [,3]
[1,] 0.08333333 0.3333333 0.5833333
[2,] 0.16666667 0.4166667 0.6666667
[3,] 0.25000000 0.5000000 0.7500000
```

`fractional(sx2) ## reinstated`

```
[,1] [,2] [,3]
[1,] 1/12 1/3 7/12
[2,] 1/6 5/12 2/3
[3,] 1/4 1/2 3/4
```

`solve(xr) %>% fractional ## xr is non-singular with a simple inverse`

```
[,1] [,2] [,3]
[1,] 7/18 -10/9 3/4
[2,] -35/9 160/9 -15
[3,] 14/3 -70/3 21
```

`numerators(x2) ## numerators and denominators are available`

```
[,1] [,2] [,3]
[1,] 1 1 49
[2,] 1 25 4
[3,] 1 1 9
```

`denominators(x2)`

```
[,1] [,2] [,3]
[1,] 144 9 144
[2,] 36 144 9
[3,] 16 4 16
```

The *golden ratio* of the ancient Greeks, \(\phi = \left(\sqrt{5}+1\right)/2 = 1.61803398874989490\dots\) is well-known to be the limit of the ratio of successive Fibonacci numbers. As a partial indication of this result, the following demonstration is at least suggestive:

```
F <- c(1,1,numeric(15))
for(i in 3:17) ## Fibonacci sequence by recurrence
F[i] <- F[i-1] + F[i-2]
F
```

```
[1] 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610
[16] 987 1597
```

`(phi <- (sqrt(5) + 1)/2)`

`[1] 1.618034`

The function * vfractional* may be used to provide a sequence of optimal

`vfractional(phi, eps = 0, maxConv = 1:16)`

```
[1] 1 2 3/2 5/3 8/5 13/8 21/13 34/21
[9] 55/34 89/55 144/89 233/144 377/233 610/377 987/610 1597/987
```

With random numbers it is mildly interesting from a theoretical point of view to look at the distribution of the number of convergents in the continued fraction expansion needed to achieve a specific error tolerance. We have the tools to explore this somewhat arcane issue:

```
library(ggplot2)
N <- 500000
set.seed(3210)
rat(rnorm(N), eps = 1.0e-07)[, "n"] %>%
table(x = .) %>%
as.data.frame(responseName = "f") %>%
ggplot(.) + aes(x = x, y = f/N) +
geom_bar(stat = "identity", fill = "steel blue") +
ylab("Relative frequency") + xlab("Convergents")
```

In particular this appears to justify the choice of `maxConv = 20`

as a suitable default^{2}.