Calculators

Dated Feb 17, 2025; last modified on Mon, 17 Feb 2025

Android Calculator

Making a precise calculator is not trivial. While \(10^{100} + 1 - 10^{100} = 1\), iOS’s calculator gives back \(0\). explores how Android’s calculator gets this right courtesy of .

Real number representations are fundamentally imprecise because we can’t squeeze in infinitely many real numbers into a finite number of bits. Floating-point representations have a base \(\beta\) (assumed to be even) and a precision \(p\), e.g., if \(\beta = 10\) and \(p = 3\), then \(0.1\) is represented as \(1.00 \times 10^{-1}\). IEEE 754, a dominant floating point representation, requires \(\beta = 2, p = 24\) for single precision and \(p = 53\) for double precision. In IEEE 754, \(0.1\) cannot be represented exactly as \(1/10\) is not a factor of \(2\); it is approximately \(1.10011001100110011001101 \times 2^{-4}\).

BigInteger representations support as big a number as the host computer’s memory allows.

pub struct BigNum {
  /// true for positive, false for negative
  sign: bool,

  /// Base 1e9 digits, least significant first
  digits: Vect<u32>,
}

With BigNum, the calculator can handle all integers \(\mathbb{Z}\), e.g., \(10^{100} + 1 - 10^{100}\).

Rational numbers \(\mathbb{Q}\) are numbers that can be expressed as a ratio of an integer to a non-zero integer. All integers are rational, but not all rational numbers are integers, e.g., \(\frac{2}{9}\). Rational numbers can be handled by using BigNums for the numerator and denominator.

Why is \(\pi\) considered irrational? Isn’t \(22/7\) rational?

Well, \(22/7\) is a rational approximation to \(\pi\) that is commonly taught in schools. \(\pi = 3.14159265 \ldots \) while \( 22/7 = 3.14285714 \ldots \).

Real algebraic numbers include ones that can’t be expressed as fractions, e.g., \(\sqrt{2}\). They can be represented as the polynomial equation they satisfy, e.g., \(\sqrt{2}\) can be expressed as \(x^2 - 2 = 0\) with a note that you want the positive root. Addition involves creating a new polynomial that has their sum as a root, and multiplication involves using polynomial composition and resultants.

To handle real numbers (including transcendental ones like \(\pi\) and \(e\) that are not the root of a non-zero polynomial with rational coefficients) , recursive real arithmetic (RRA) defines each number as a function that takes a requested tolerance and computes a rational guaranteed to be within tolerance of the real number it represents, e.g., for \(\pi\):

const pi = (tolerance) => {
  let sum = 0;
  let i = 0;
  let term = 1;

  // Leibniz formula: π/4 = 1 - 1/3 + 1/5 - 1/7 + ....
  while (Math.abs(term) > tolerance) {
    term = 1 / (2 * i + 1) * (i % 2 === 0 ? 1 : -1);
    sum += term;
    i++;
  }

  return sum * 4;
}

However, RRA can only tell you that \(1 - 1\) is within a rounding error of \(0.0000000000000\), but the user expects to see exactly \(0\). Checking whether two equal RRA numbers are indeed equal doesn’t terminate as you’ll infinitely keep computing them to finer precision. That said, not all constructive reals can be expressed with operations available on a calculator. Only these are:

  • The 4 basic arithmetic operations, and square roots.
  • \(sin, cos, tan\) trigonometric functions and their inverses.
  • Exponential and (natural) logarithm functions

While ’s algorithm can detect that \(1 \ne 1 - e^{-e^{1000}}\), the algorithm requires more steps than there are atoms in the universe.

If the user enters \(6 \times 9\), only use rational arithmetic; however, when irrationals like \(\pi\) or \(\sqrt{2}\) come into play, use RRA. Even further, we can have symbolic representations to use in place of RRA functions, e.g.,

enum Real {
  One,
  Pi,
  Exp(Rational),
  Log(Rational),
  Sin(Rational),
  // ...
  Other(RRA)
}

… so that in \((1 \times \pi) + (3 \times \pi)\) can use the symbolic representation of \(\pi\); the summation can be done by adding the rational parts. For a computation like \((1 \times \pi) + (3 \times \sqrt{2})\), we’d still need RRA. With this formulation, we have a calculator that is 100% correct, with mostly the correct UX and without the implementation complexity of a computer algebra system.

Impressed by ’s breakdown of to showcase the tradeoffs and alternatives considered.

References

  1. Towards an API for the Real Numbers. Hans-J Boehm. dl.acm.org . research.google . Accessed Feb 17, 2025.
  2. A calculator app? Anyone could make that. Chad Nauseam. chadnauseam.com . Feb 15, 2025. Accessed Feb 17, 2025.
  3. What Every Computer Scientist Should Know About Floating-Point Arithmetic. David Goldberg. docs.oracle.com . 1991. Accessed Feb 17, 2025.
  4. (22/7) is a rational number and (π) is irrational number. math.stackexchange.com . Accessed Feb 17, 2025.
  5. The Identity Problem for Elementary Functions and Constants. Dan Richardson; John Fitch. International Symposium on Symbolic and Algebraic Computation. dl.acm.org . Aug 1, 1994. Accessed Feb 17, 2025.
  6. List of types of numbers. en.wikipedia.org . Accessed Feb 17, 2025.