Operators + - * / ( ) · functions sin cos tan asin
acos atan exp ln sqrt abs max min · constants pi e
What is a constructive real?
A floating-point number has limited precision: 53 bits of mantissa, fixed at
the moment each operation rounds. A constructive (or computable) real
is a program: a function that, given a precision
p, returns an integer n such that
n·2p is within 2p of
the true value. The digits slider controls how many digits of precision the program should compute.
How results are represented
When you type sqrt(2)*sqrt(2) - 2, the calculator first builds a term graph — multiplication node,
square-root nodes, integer leaves — which you can inspect under
"How is this represented?" on each result. Evaluation is lazy and
demand-driven: asking the root for p bits makes each node
ask its children for enough extra precision to keep its own error
bound, all the way down to the leaves.
Each node also memoizes the best approximation it has produced so far
(shown as ≈ value in the graph). Any time you ask for more digits the cached value deepens, and when you ask for less it reuses the cached answer. Terms that nothing
has demanded yet are marked not evaluated — for instance, an
expression's structure can be built and displayed without ever
computing a single digit.
Where floats go wrong
Every float operation rounds to 53 bits, and the errors compound in characteristic ways, all reproducible above:
- Representation error:
0.1and0.2have no finite binary expansion, so0.1 + 0.2adds two values close to, but not exactly the numbers entered. The sum is halfway between two representable floats (0.3 and 0.30000000000000004), and IEEE 754’s ties-to-even rule chooses the upper one. - Catastrophic cancellation: subtracting nearly
equal quantities, as in
sqrt(2)*sqrt(2) - 2, cancels the correct leading digits and promotes the rounding noise to the front of the answer. - Absorption:
1016 + 1rounds back to1016: the 1 is smaller than one unit in the last place, so it simply vanishes.
The float column isn't a simulation, by the way: this page compiles the calculator to JavaScript, so the float result is computed with actual JavaScript numbers — the same IEEE 754 doubles nearly every language uses.
The catch: equality
The price of never rounding is that equality of two constructive
reals is undecidable. If two computations happen to denote the same
number, comparing them digit-by-digit produces agreement forever and
never terminates — you can't distinguish "equal" from "differs at some
digit you haven't reached yet". So the API offers comparison up to a
tolerance, and an equality that is only safe when the numbers are
known to be unequal. Hans-J. Boehm's paper, which this library
implements, shows how to recover decidable equality for the
common cases by additionally tracking numbers symbolically — the trick
that makes a real-number type practical for a calculator, where
sqrt(2)*sqrt(2) - 2 should display exactly
0. (Relatedly: dividing by an exact zero here doesn't
hang searching for a nonzero digit — it gives up with a precision
overflow once the sign search passes a depth bound.)
This isn't just a demo trick
This representation ships in Google's Android calculator app, with over a billion installs: there, scrolling a result sideways drives the precision demand, and users never see a wrong digit. The design is described in Towards an API for the Real Numbers (Boehm, PLDI 2020); there's also a nice Twitter exposition of the main ideas. This page runs an OCaml implementation of the paper, compiled to JavaScript with js_of_ocaml (bignums via zarith_stubs_js), with all evaluation in a Web Worker so the page stays responsive while you ask π for 10,000 digits.