I have been working on a Rust crate of this approach named `realistic`. It's interesting to work with, the crate provides a very rudimentary text interface, type in expressions like
(* (sqrt 45) (sqrt 30) (sqrt 50))
And it will tell you both that this is approximately 259.80 (by default it gives IIRC 30 decimal places of approximation) and that it's exactly 150 times the square root of 3.
I have a toy text calculator that's in a separate project since it has a lot of dependencies and I'm not sure how it should look, but the core "API for the Real Numbers" functionality lives in realistic.
The core idea of this API is: What if we augment the Computable Real Numbers with a tag which says what kind of computable number it is, and a Rational multiplier ?
This way we can express some of the things you learned in school like multiplying the square root of two things might have an integer result even if the two square roots weren't integers, yet we can also Compute a decimal expansion where appropriate to as many places as we can afford.
This is not an alternative to the floating point numbers, those are basically a subset of the rationals with hardware acceleration and good PR. If floating point is what you need, this doesn't help you. Indeed, if purely rational numbers are enough you only need an ordinary Big Rational Number library, lots of those exist, find one which suits your need. realistic has its own Rational type, but that's mostly because it's where I started, you definitely shouldn't choose realistic::Rational as your numerical type if you don't need realistic::Real or realistic::Computable
You can loop on increasing precision using things like FLINT's ball arithmetic [0], which maintain an absolute error bound alongside the value. GNU MPFR uses a similar strategy internally, increasing the precision by a factor of 1.5 each iteration, though it also special-cases a few functions known to have problematic inputs.
But I don't think the zero-testing problem will ever be solved to everyone's satisfaction. Even CAS software like Mathematica falls back to a fallible heuristic and spits out a warning if it's unsure, and it only takes a few tricky exponentials to leave the range of what it recognizes.
(* (sqrt 45) (sqrt 30) (sqrt 50))
And it will tell you both that this is approximately 259.80 (by default it gives IIRC 30 decimal places of approximation) and that it's exactly 150 times the square root of 3.
I have a toy text calculator that's in a separate project since it has a lot of dependencies and I'm not sure how it should look, but the core "API for the Real Numbers" functionality lives in realistic.
The core idea of this API is: What if we augment the Computable Real Numbers with a tag which says what kind of computable number it is, and a Rational multiplier ?
This way we can express some of the things you learned in school like multiplying the square root of two things might have an integer result even if the two square roots weren't integers, yet we can also Compute a decimal expansion where appropriate to as many places as we can afford.
This is not an alternative to the floating point numbers, those are basically a subset of the rationals with hardware acceleration and good PR. If floating point is what you need, this doesn't help you. Indeed, if purely rational numbers are enough you only need an ordinary Big Rational Number library, lots of those exist, find one which suits your need. realistic has its own Rational type, but that's mostly because it's where I started, you definitely shouldn't choose realistic::Rational as your numerical type if you don't need realistic::Real or realistic::Computable