and gets harder when you want exact irrationals too
But maybe that just results in all the floating point weirdness again, just not for small rationals.
One thing you can try is storing a floating point numerator and a floating point denominator, and renormalizing them by bit shifts instead of finding GCDs. This lets you avoid rounding errors for small ratios. For general purposes this advantage isn’t really worth doubling the number of bits and complicating arithmetic for though.
See e.g. https://observablehq.com/@jrus/qang