A calculator that doesn’t round | Constructive Calculator
← Constructive Calculator<br>No Rounding
Constructive Calculator is an iPhone calculator that doesn’t round at all.<br>It computes with constructive real numbers: every result is exact, and you can scroll<br>any answer for as many correct digits as you want. What that gives you:
exp(π√163), Ramanujan’s constant, looks like the<br>integer 262,537,412,640,768,744. Scroll past the decimal point and you find<br>twelve 9s before it finally diverges.
exp(100) + 42 − exp(100) returns exactly 42 . In IEEE 754 double,<br>exp(100) ≈ 2.7×1043, and adding 42 changes nothing (42<br>falls off the bottom of the representation), so the subtraction gives 0 . The constructive<br>engine evaluates exp(100) to as many digits as is needed, twice, and the subtraction<br>leaves only 42. The calculator does not simplify the expression first; it does indeed<br>perform both operations as instructed: addition and then subtraction.
It’s made possible by constructive (or computable) real arithmetic: instead of<br>storing a number as a fixed-width approximation, store a function that produces an approximation<br>to any requested precision, and evaluate each subexpression to whatever precision the final<br>answer needs. Hans Boehm built a Java library for this in the 1980s and 90s, and it has been<br>the engine behind Android’s built-in Calculator, a fact that periodically<br>delights Hacker<br>News.
But there was no equivalent on iPhone that I could find, so I built one, by porting<br>Boehm’s engine.
It’s 2026, so I didn’t hand-write the port. I directed Opus 4.8 to translate the source<br>line by line into Swift, and steered the process (architecture, UI, on-device testing, the<br>App Store machinery) over a long back-and-forth.
What got ported:
com.hp.creals, Boehm’s constructive-reals library (CR,<br>the transcendental functions, the Gauss-Legendre AGM for π), into a Swift package.
UnifiedReal / BoundedRational from AOSP’s<br>ExactCalculator, the layer that keeps results symbolically exact when it can (so<br>cos(π/3) comes back as exactly ½, not a constructive<br>approximation of it) and makes comparisons decidable for the rational cases.
The expression evaluator and a SwiftUI front end with the scroll-for-more-digits<br>display.
The interesting work was what didn’t translate directly. Java’s<br>synchronized, checked exceptions, and AsyncTask have no Swift<br>equivalents, so the port re-expressed them: exceptions became Swift throws,<br>precision-overflow and divergence became typed errors, and Java’s interrupt-based<br>cancellation became Swift Concurrency’s Task.checkCancellation(). The 2013<br>Mac I develop on can’t run a current Xcode, so signing and TestFlight uploads run on<br>GitHub Actions; the local machine never touches the submission.
Then I had a different model, Fable 5, do a clean-room review: fresh context, no<br>memory of how the code was built, just the repository. It earned its keep:
A concurrency bug : @MainActor was attached to the wrong type, so the<br>view model wasn’t main-actor isolated and a background task was mutating<br>@Published state off the main thread.
A subtler one: Boehm’s Java get_appr (the memoized approximation<br>routine) is synchronized; the port had dropped the lock and leaned on an actor<br>to serialize access, but two paths slipped around the actor, so two threads could race the<br>same constructive real’s cache. For shared singletons like π that is a<br>memory-safety bug, not just a wrong digit. The fix was the faithful one: put the lock back<br>(reentrant, since the square-root routine re-enters itself).
A main-thread freeze : a large factorial was evaluated synchronously, hanging the<br>UI with no way to cancel. It now runs off the main thread, cancellable, with a sane cap.
And the boring-but-mandatory App Store gaps: a missing privacy manifest, the<br>export-compliance flag, accessibility labels.
As a data point on AI-assisted development, I found this quite useful: the port<br>itself was faithful (Fable 5 checked the algorithms against Boehm’s Java and<br>found no transcription errors), but the adaptation to a different concurrency model is<br>where the bugs lived, and a second model with no stake in the first one’s choices caught<br>them. None would have shown up in the unit tests.
Known Limitations
Exact real arithmetic has a hard wall: equality is undecidable . You cannot, in<br>general, prove two constructive reals are equal by any finite computation. So<br>exp(100)+42−exp(100) is correctly 42, but the app can’t prove<br>it terminates; it prints 42.000… with a “more digits” arrow<br>and will emit zeros forever. It always gives you the right number to as many digits as you<br>want; it only labels a result “exact” when the value stays inside the<br>structured rational-times-known-constant form. cos(π/3)=½ does;<br>exp(100)+42 doesn’t....