## `numeric::rationalize` -- approximate a floating point number by a rational number

#### Introduction

`numeric::rationalize``(object, ..)` replaces all floating point numbers in `object` by rational numbers.

#### Call(s)

`numeric::rationalize(object <, mode> <, digits>)`

#### Parameters

`object` | - | an arbitrary MuPAD object

#### Options

``mode`` | - | either Exact, or Minimize, or Restore. This controls the strategy for approximating floating point numbers by rational numbers.
``digits`` | - | a positive integer (the number of decimal digits) not bigger than the environment variable `DIGITS`. It determines the precision of the rational approximation.

#### Returns

If the argument is an object of some kernel domain, then it is returned with all floating point operands replaced by rational numbers. An object of some library domain is returned unchanged.

### Overloadable:

`object`

#### Side Effects

The function is sensitive to the environment variable `DIGITS`.

#### Details

- An object of a library domain, characterized by

             domtype(extop(object,0))=DOM_DOMAIN,

  is returned unchanged. For all other objects `numeric::rationalize` is applied recursively to all operands. Objects of library domains can be rationalized, if the domain has an appropriate `map` method. Cf. example 5.

#### Option: `digits`

- A floating point number f is approximated by a rational number r satisfying abs(f-r)<eps*abs(f).

  With the options Exact and Minimize the guaranteed precision is eps=10^(-`digits`). With Restore the guaranteed precision is only eps=10^(-`digits`/2).

- The default precision is `digits=DIGITS`.
- The user defined precision must not be larger then the internal floating point precision set by `DIGITS`: an error occurs for `digits>DIGITS`.

#### Option: Exact

- Any real floating point number f<>0.0 has a unique representation

      f=sign(f)*mantissa*10^(exponent)

  with integer exponent and 1.0<= mantissa<10.0. With the option Exact the float mantissa is replaced by the rational approximation

      round(mantissa*10^digits)/10^digits.

  This guarantees a relative precision of `digits` significant decimals of the rational approximation.
- This is the default strategy, so there is no real need to pass this option as a parameter to `numeric::rationalize`.

#### Option: Minimize

- This strategy tries to minimize the complexity of the rational approximation, i.e., numerators and denominators are to be small.
- The guaranteed precision of the rational approximation is `digits`.
- Cf. example 3.

#### Option: Restore

- This strategy tries to restore rational numbers obtained after elementary arithmetical operations applied to floating point numbers. E.g., for rational `r` the float division `f = 1/float(r)` introduces additional round-off, which the Restore algorithm tries to eliminate: `numeric::rationalize``(f, Restore) = 1/r`. This strategy, however, is purely heuristic and will not succeed, when significant round-off is caused by arithmetical float operations!

- The guaranteed precision of the rational approximation is only `digits/2`!

- Cf. example 4.

#### Example 1

`numeric::rationalize` is applied to each operand of a composite object:

    >> numeric::rationalize(0.2*a+b^(0.7*I))

                                    a    7/10 I
                                    - + b
                                    5

    >> numeric::rationalize([{poly(0.2*x, [x]), sin(7.2*PI) + 1.0*I},
                             exp(3 + ln(2.0*x))])

          -- {                      / 36 PI \     }                   --
          |  { poly(1/5 x, [x]), sin| ----- | + I }, exp(ln(2 x) + 3)  |
          -- {                      \   5   /     }                   --

#### Example 2

We demonstrate the default strategy Exact:

    >> numeric::rationalize(12.3 + 0.5*I),
       numeric::rationalize(0.33333),
       numeric::rationalize(1/3.0)

              123/10 + 1/2 I, 33333/100000, 33333333333/100000000000

    >> numeric::rationalize(10^12/13.0),
       numeric::rationalize(10^(-12)/13.0)

                76923076923, 76923076923/1000000000000000000000000

We reduce the precision of the approximation to 5 digits:

    >> numeric::rationalize(10^12/13.0, 5),
       numeric::rationalize(10^(-12)/13.0, 5)

                     76923100000, 769231/10000000000000000000

#### Example 3

We demonstrate the strategy Minimize for minimizing the complexity of the resulting rational number:

    >> numeric::rationalize(1/13.0, 5),
       numeric::rationalize(1/13.0, Minimize, 5),
       numeric::rationalize(0.333331, 5),
       numeric::rationalize(0.333331, Minimize, 5),
       numeric::rationalize(14.285, 5),
       numeric::rationalize(14.2857, Minimize, 5),
       numeric::rationalize(1234.1/56789.2),
       numeric::rationalize(1234.1/56789.2, Minimize)

          769231/10000000, 1/13, 333331/1000000, 1/3, 2857/200, 100/7,

             21731244673/1000000000000, 12341/567892

We compute rational approximations of PI with various precisions:

    >> numeric::rationalize(float(PI), Minimize, i) $ i = 1..10

          3, 22/7, 22/7, 355/113, 355/113, 355/113, 355/113,

             102573/32650, 104348/33215, 208341/66317

#### Example 4

We demonstrate the strategy Restore for restoring rational numbers after elementary float operations. In many cases also the Minimize strategy restores:

    >> numeric::rationalize(1/7.3, Exact),
       numeric::rationalize(1/7.3, Minimize),
       numeric::rationalize(1/7.3, Restore)

                      13698630137/100000000000, 10/73, 10/73

However, using Restore improves the chances of recovering from round-off effects:

    >> numeric::rationalize(10^12/13.0, Minimize),
       numeric::rationalize(10^12/13.0, Restore)

                           76923076923, 1000000000000/13

    >> numeric::rationalize(123.456/12.34567, Minimize),
       numeric::rationalize(123.456/12.34567, Restore)

                          529097/52910, 12345600/1234567

In some cases Restore manages to recover from round-off error propagation in composite arithmetical operations:

    >> x := 125/12.34567: y := 123/12.34567: z := (x^2 - y^2)/(x + y)

                                   0.1620001183

    >> numeric::rationalize(z, Minimize),
       numeric::rationalize(z, Restore)

                           35612/219827, 200000/1234567

The result with Restore corresponds to exact arithmetic:

    >> rx := numeric::rationalize(x, Restore):

    >> ry := numeric::rationalize(y, Restore):

    >> (rx^2 - ry^2)/(rx + ry)

                                  200000/1234567

Note that an approximation with Restore may have a reduced precision of only `digits/2`:

    >> x := 1.0 + 1/10^6:

    >> numeric::rationalize(x, Exact),
       numeric::rationalize(x, Restore)

                                1000001/1000000, 1

    >> delete x, y, z, rx, ry:

#### Example 5

The floats inside objects of library domains are not rationalized directly. However, for most domains the corresponding `map` method can forward `numeric::rationalize` to the operands:

    >> Dom::Multiset(0.2, 0.2, 1/5, 0.3)

                          {[0.3, 1], [0.2, 2], [1/5, 1]}

    >> numeric::rationalize(%), map(%, numeric::rationalize, Restore)

               {[0.3, 1], [0.2, 2], [1/5, 1]}, {[1/5, 3], [3/10, 1]}

#### Background

- Continued fraction (CF) expansion is used with the options Minimize and Restore.
- With Minimize the first CF approximation satisfying the precision criterion is returned.
- The Restore algorithm stops, when large coefficients of the CF expansion are found.

#### Changes

- used to be called `sharelib::rational`
- Option Approx is now called Minimize. The option Restore was introduced.

Do you have questions or comments?
Copyright © SciFace Software GmbH & Co. KG 2000