not exactly, unless you consider space efficiency to be an aspect of performance (which is certainly reasonable). a naive implementation of rationals using two int32_t's only covers the range of a single int32_t, despite using as many bits as the double. it's also a trade-off between range and consistent precision, of course.
this certainly isn't some deep insight into number representation, just a quick point for the benefit of people who haven't thought much about rational data types before.
You can store slightly fewer numbers with rationals, because it's hard to avoid having a representation for both 2/4 and 3/6. But the loss of range or precision due to that is pretty small.
Let's say we need to do a comparison. Set
a = 34241432415/344425151233
and b = 45034983295/453218433828
Which is greater?Or even more feindish, Set
a = 14488683657616/14488641242046
and b = 10733594563328/10733563140768
which is greater?By what algorithm would you do the computation, and could you guarantee me the same compute time as comparing 2/3 and 4/5?
a/b > x/y is the same as ay > xb
Assuming you don’t overflow your integer type.
There's your answer :)
It's far too easy to overflow your integer type by simply adding a bunch of rationals whose denominators happen to be coprime, or just by multiplying rationals. For this reason, the vast majority of rational implementations use arbitrary precision integers, and of course arithmetic on those isn't constant time.
The problem is devs who don't understand what they're doing and just think that they can use floats in every situation and it'll just work out fine. This is not helped by many popular scripting languages who just default to floats when a result doesn't fit an integer (something more reasonable languages like Common Lisp don't do for instance).
For instance, to speak of videogames again, very tight precision isn't usually an issue but loss of granularity when numbers get very big can cause problems, especially if you have very large levels. That being said rationals wouldn't really help you here, you'd have the same problem except now you have to keep two numbers within bound instead of one. Imagine having a very small offset in a complex operation and ending up with a number like 100000000000000000000000000/100000000000000000000000001 !