R8 — Exact arithmetic
Goal: get answers that are exactly right rather than nearly right, and understand the two different numbers both called “precision”.
Answering R1’s question
R1 noted that 2 + 3 * 4 comes back as 14.0 and promised this lesson would explain it. Here is the short version:
> 2 + 3 * 4
14.0
> exact on
exact = on
> 2 + 3 * 4
14
By default every number is a decimal — a floating-point value, which is why a whole result still carries .0. Turn exact mode on and 14 is an exact integer, so it prints as one.
That is not a display setting. It changes what the numbers are.
Fractions instead of decimals
> 1/3
1/3
> 1/3 + 1/6
1/2
> (2/3)^10
1024/59049
> harmonic(4)
25/12
1/3 is now a fraction rather than 0.33333, and it stays one through arithmetic. No rounding happened anywhere above, so nothing accumulated.
Exactness survives a fold, too — this is a sum of ten fractions:
> sum(1/k, k, 1, 10)
7381/2520
Why the switch has to come first
> 0.1 + 0.2
3/10
Exactly three tenths — where floating point cannot represent one tenth at all.
That is why exact on is a parse-time switch: it changes what a literal becomes as you type it. Turning it on afterwards could not help, because by then the tenth you meant is already gone. Set it before you enter the numbers.
The error you cannot see
Turn it off again and look at the same sum:
> exact off
exact = off
> precision 15
precision = 15
> 0.1 + 0.2
0.3
> (0.1 + 0.2) - 0.3
0.0
0.1 + 0.2 is genuinely not three tenths, and yet it prints as 0.3 — and the difference prints as 0.0 — at the highest precision the REPL will display.
That is the honest shape of the problem. The error is real, about 5.6e-17, and below anything you can be shown. You cannot audit it by looking, and it accumulates in long computations where nobody is looking. Removing it is more reliable than inspecting for it.
Irrationals, and the working precision
An irrational is not a fraction, so there is nothing exact to store. Leonardo computes one to a working precision and carries on:
> exact on
exact = on
> exact precision 40
exact precision = 40
> pi * e
8.539734222673568
The value carries forty digits; the display shows fifteen, because that is the most a printed decimal here will claim.
The two precisions are different quantities
> exact off
exact = off
> precision 3
precision = 3
> 1/3
0.333
> precision 12
precision = 12
> 1/3
0.333333333333
precisionis how many digits are shown. The same value twice above, displayed two ways — nothing about the computation changed.exact precisionis how many digits an irrational is computed to before exact arithmetic continues.
They are separate deliberately: if raising the one you read also raised the one you compute with, then asking to see more digits would silently change your results.
Either reports itself when typed bare:
> exact
exact = off, working precision = 40
Where it earns its keep
A Double holds about fifteen significant digits. Past that it stops counting and starts estimating:
> precision 15
precision = 15
> 2^100
1.2676506002282294E30
> fact(25)
1.5511210043330986E25
Those are approximations in scientific notation — the true values are integers with 31 and 26 digits. Exactly:
> exact on
exact = on
> 2^100
1267650600228229401496703205376
> fact(25)
15511210043330985984000000
Every digit, because nothing was ever rounded. This is the clearest case for the tier: whenever an answer needs more than fifteen digits, floating point cannot give it and exact arithmetic can.
What it is not for
It is not a substitute for a well-chosen algorithm. This quadratic has roots near -1e8 and -1e-8, and the small one is the classic casualty of the textbook formula:
> exact off
exact = off
> solve(x^2 + 1e8*x + 1 = 0, x)
x_1 := -1.0E8
x_2 := -1.0E-8
> unset x_1
x_1 unset
> unset x_2
x_2 unset
The small root is right, in ordinary floating point, because Leonardo already uses the stable rearrangement of the quadratic formula rather than the one that cancels. Turning exact mode on would not have rescued you had it used the naive form, and it was not needed given it does not. Exact arithmetic buys digits; it does not buy numerical judgement.
Turning it off
> exact off
exact = off
> precision 5
precision = 5
> 1/3
0.33333
Exact mode is off by default, and a session that never touches it behaves exactly as before, so there is no cost to leaving it alone.
Try it yourself
Is (1/7) * 7 exactly one? Check it both ways.
Answers
```text > (1/7) * 7 1.0 > exact on exact = on > (1/7) * 7 1 > exact off exact = off ``` *[run this session](https://lycogrypho.github.io/Leonardo/app/#s=precision%205%0Acolors%20dark%0Apretty%20off%0Alatex%20off%0Anames%20off%0Alogic%20minmax%0Alogic%20symmetric%20off%0Aexact%20precision%2040%0Aexact%20off)* Both print `1` — floating point is lucky here, because the two roundings happen to cancel. **Exactness is about the guarantee, not about whether a particular case gets away with it**, which is the whole argument for having the tier at all.What you learned
exact onmakes literals exact — fractions stay fractions, and14is an integer. It is a parse-time switch, so set it first.- Irrationals are computed to
exact precisiondigits and then carried on with. precisionshows,exact precisioncomputes. Separate on purpose.- The floating-point error is usually too small to display and too real to ignore.
- It earns its keep past fifteen significant digits, where a
Doublestops counting — but it is no substitute for a well-conditioned algorithm. - Off by default; leaving it off changes nothing.
Previous: R7 — Drawing and sampling · Next: R9 — Your session · All lessons