<p>(By rationals, I mean fractions. So, <code>0.5</code> would be stored as <code>1/2</code>.)</p>
<p>There's only one language I know of which does this. So, what are the advantages and disadvantages of it?</p>

<numbers>
What are the advantages and disadvantages of implementing reals as rationals?
CC BY-SA 4.0
2023-06-28T10:56:31.923Z
Every single CAS language does this, for very obvious reasons.
SK-logic
CC BY-SA 4.0
2023-06-28T12:18:15.707Z
It would be interesting for a rational number's representation to include an extra 64 bits for a floating-point value, and have all operations perform both calculations. It's already big and slow, so it wouldn't make much difference to the performance, but it would make it easy to observe how much difference the two values have when the results are displayed. (e.g. for a specific application, confirming that the rational numbers *do* give a significantly better answer, or discovering that they really weren't needed.)
Ray Butterworth
CC BY-SA 4.0
2023-06-29T08:39:56.030Z
@RayButterworth if you want to see how much precision you're losing by using floating point, there is interval arithmetic for you. It can even be fast and hardware-assisted.
SK-logic
CC BY-SA 4.0
2023-07-03T20:56:13.983Z
@TheThonnu: What is that one language?  ABC (a predecessor to Python)?
dan04
CC BY-SA 4.0
2023-07-04T05:25:03.577Z
@dan04 it's [Vyxal](https://vyxal.pythonanywhere.com/?v=1#WyIiLCIiLCIuNSIsIiIsIiJd), a stack-based golfing language.
The Thonnu
CC BY-SA 4.0
2023-05-16T17:10:27.187Z
Do you understand what "floating point" means? Also, what language are you thinking of?
Solomon Ucko
CC BY-SA 4.0
2023-05-16T17:11:30.670Z
@SolomonUcko I thought it meant numbers which have a decimal place. Do you want me to change it to "real"? And I'm thinking of [Vyxal](https://vyxal.pythonanywhere.com/?v=1#WyIiLCIiLCIuNSIsIiIsIiJd).
The Thonnu
CC BY-SA 4.0
2023-05-16T17:42:17.137Z
Lots of language support both. Most Lisps I know use rationals and devolve into floats when you do something like `sqrt` that's inherently imprecise. Raku works similarly.
Silvio Mayolo
CC BY-SA 4.0
2023-05-17T02:11:35.230Z
@TheThonnu, there is *fixed point*, which has the decimal point in a fixed position, and *floating point* where the point can be in any position. Business languages (e.g. COBOL) make good use of fixed point.  E.g. `COST` could be defined as a fixed point number with 2 digits after the decimal (suitable for storing money, without the round-off problems that floating-point values would have). Most other languages restrict all fixed point values to always having no digits after the decimal.
Ray Butterworth
CC BY-SA 4.0
2023-05-17T02:12:02.917Z
Do you mean reals are written in decimal but stored as rationals?
alephalpha
CC BY-SA 4.0
2023-05-17T05:31:18.017Z
@alephalpha yes, in the program they are written like `.5`, but stored and output like `1/2`.
The Thonnu
<h2>Advantages</h2>
<ul>
<li>Rationals can exactly represent a much wider range of numbers. One-third would be finitely representable.</li>
<li>Floating point errors would be unheard of.</li>
</ul>
<h2>Disadvantages</h2>
<ul>
<li>It is computationally much more expensive to compute with arbitrary-sized rationals.</li>
<li>If you do not reduce them, they may take up a lot more memory, even with the same number. <code>1/1</code> takes up a lot less space than <code>100000/100000</code>.</li>
</ul>

CC BY-SA 4.0
2023-05-16T17:40:09.963Z
2023-05-16T18:53:29.607Z
2023-05-16T18:53:29.607Z
Seggan
CC BY-SA 4.0
2023-06-28T16:38:59.120Z
Is the computational penalty just a legacy? *If* we had good reasons to always use rationals instead of floating-point, then processors would have built-in rational arithmetic, and rational arithmetic would be as fast as floating-point arithmetic, wouldn't it? Or would it still be prohibitively expensive to have to compute `gcd` too often to simplify fractions?
Stef
CC BY-SA 4.0
2023-07-05T16:15:59.700Z
@Stef: The technical difficulty with hardware rational support is that you'd need support for arbitrary-sized integers, which aren't suited to fixed-size registers and ALUs.  Unless you imposed a restriction like “numerator and denominator must each fit in 64 bits”, but then you'd lose exact arithmetic.
dan04
CC BY-SA 4.0
2023-07-19T15:18:57.130Z
For a given representation size, floating-point can represent _more_ numbers (and a bigger range), because every non-NaN float represents a distinct value.  There's holes in the rational space, because, e.g. ²⁄₄ == ¹⁄₂.
Toby Speight
CC BY-SA 4.0
2023-05-16T18:31:07.237Z
Worth stressing that there's not only a little but rather a huge gulf between the efficiency of computing with floats (with support built into the processor) and arbitrary-size rationals.
Noldorin
<p>I am implementing a language that uses rationals by default. Let me tell you why.</p>
<p>The other answers are correct that performance and memory use are worse with rationals. My rationals are 32 bytes against an 8-byte, 64-bit floating point double.</p>
<p>And yet most uses of programming languages are for one-off scripts. The vast majority, in fact. In those cases, the programmer/user just wants to accomplish the task at hand and move on, not caring about performance because it's probably &quot;good enough.&quot;</p>
<p>It's best if they don't have to worry about stupid issues like overflow, underflow, catastrophic cancellation, or any such type of bug.</p>
<p>So by default, my language uses rationals. In fact, users will also be able to attach units to numbers and will handle all conversions itself; the user just says the input units and the output units.</p>
<p>This will avoid bugs for the 99% case of a quick script when performance doesn't matter.</p>
<p>Of course, it will also have floating point, fixed-point, and standard integers. You'll also be able to attach units to those.</p>
<p>So yes, the cons the other answers listed exist. But the biggest pro of rationals, in my opinion, is that they help the user avoid bugs.</p>

CC BY-SA 4.0
2023-06-28T11:24:47.250Z
2023-06-28T11:24:47.250Z
Gavin D. Howard
CC BY-SA 4.0
2023-06-29T05:25:49.937Z
Are you aware of the [Frink](https://frinklang.org/) programming language? It sounds like there is some overlap regarding [numeric types](https://frinklang.org/#NumericTypes) (including [intervals](https://frinklang.org/#IntervalArithmetic)) and [units](https://frinklang.org/#HowFrinkIsDifferent) and [unit conversions](https://frinklang.org/#Conversions).
Jörg W Mittag
CC BY-SA 4.0
2023-06-29T11:38:22.853Z
Very aware. I was going to generate my units from Frink's incredibly awesome and downright snarky units file. The thing with Frink is that it focuses on being a `bc`-like language rather than a general-purpose one, so I'm making a more general-purpose one. And yes, I'll have intervals too.
Gavin D. Howard
<h1>Why not both?</h1>
<p>That is, make your “real” type a variant that can store either an exact rational <em>or</em> an approximate floating point value.</p>
<p>You could make rational the “default”, in that literal <code>0.1</code> would represent the fraction 1/10.  But <code>0.1f</code> would be the floating-point approximation 0xCCCCCCCCCCCCD × 2<sup>-55</sup> = 0.1000000000000000055511151231257827021181583404541015625 (assuming IEEE 754 <code>double</code>).</p>
<p>The operations <code>+</code>, <code>-</code>, <code>*</code>, and <code>/</code> on rationals, plus <code>int / int</code> division, would return a rational.  But any arithmetic involving floating-point, and any non-rationality preserving mathematical functions like trig or logarithms would return a floating-point value.</p>
<p>That way, you get exact arithmetic when you can, and approximate arithmetic when you need it.  If rational arithmetic is hurting performance for a particular algorithm, a simple typecast to <code>float</code> or multiplication by <code>1.0f</code> will force floating-point arithmetic.</p>

CC BY-SA 4.0
2023-07-05T16:09:01.160Z
2023-07-05T16:09:01.160Z
dan04
<blockquote>
<p>What are the advantages and disadvantages of implementing reals as rationals?</p>
</blockquote>
<p>The biggest disadvantage is that it is not possible. There are <a href="https://wikipedia.org/wiki/Countable_set" rel="nofollow noreferrer">countably infinitely many</a> rationals, i.e., there are exactly as many rationals as there are natural numbers. However, there are <a href="https://wikipedia.org/wiki/Uncountable_set" rel="nofollow noreferrer">uncountably infinitely many</a> reals, i.e., there are strictly more reals than there are rationals (infinitely more, in fact).</p>
<p>Therefore, reals cannot be represented as rationals. This has been known for at least 2500 years when the irrationality of √2 was proven by the Pythagoreans in Ancient Greece.</p>

CC BY-SA 4.0
2023-06-28T10:42:22.430Z
2023-06-28T10:42:22.430Z
Jörg W Mittag
CC BY-SA 4.0
2023-06-28T11:19:29.917Z
Did you see the other answers that say it is possible? This is wrong.
Starship
CC BY-SA 4.0
2023-06-28T14:40:05.507Z
@Starship-OnStrike You cannot represent *all* real numbers on a computer. This is a direct result of the cardinality issues Jörg mentions. In particular, you cannot represent the uncomputable real numbers. These are particular real numbers that you cannot represent on a computer, regardless of how you try to represent them. See the answers [here](https://math.stackexchange.com/questions/462790/are-there-any-examples-of-non-computable-real-numbers), for example. Also [this](https://en.wikipedia.org/wiki/Computable_number#Properties)
David Young
CC BY-SA 4.0
2023-06-28T16:05:58.480Z
This argument applies equally against both rational and floating-point representations, and also every other representation. In fact it's worse for IEEE 754 floating-point representations, because they are limited to a *finite* number of representable numbers, whereas rational representations using bigints can at least represent countably many numbers (subject to memory availability).
kaya3
CC BY-SA 4.0
2023-06-28T16:41:27.893Z
@kaya3-supportthestrike That is true, but it seems like this is important to mention those things when the question is "What are the advantages and disadvantages of implementing reals as ..." regardless of what follows the word "as," since the question is already not (fully) possible. This answer relates to the "implementing reals ..." part rather than the "... as rationals" part and both parts are important to the question, IMHO.
David Young
CC BY-SA 4.0
2023-06-28T16:47:32.937Z
@DavidYoung I think it is utterly unimportant. The question is about rational representations compared to other representations, not about whether reals should or shouldn't be represented by a data type at all. Even so, the fact that a representation isn't perfect doesn't mean that a representation isn't possible.
kaya3
CC BY-SA 4.0
2023-07-18T22:47:15.407Z
I value the point that rational numbers fail to represent all real numbers in a way different from IEEE 754, but I also note that [Coq](https://coq.inria.fr/distrib/current/stdlib/Coq.Reals.Reals.html) and [Lean](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Real/Basic.html) have *real* reals.
user570286
CC BY-SA 4.0
2023-07-19T22:55:38.210Z
How long would it take to add together the first 70 elements of the harmonic series using rational numbers?  How about the first thousand?  Even a 1970s Apple II using interpreted BASIC could add the first 1000 terms using 40-bit loading-point values in under 10 seconds.  Could a modern computer perform that well with rational numbers?
supercat
CC BY-SA 4.0
2023-07-25T16:04:47.753Z
@Starship-OnStrike You can argue with mathematics, but you lose.
gnasher729
<p>There are no advantages and disadvantages, because in real programs, people will need both depending on the situation.</p>
<p>For example, if you are representing money, and using floats, it may make a number slightly off in some cases, and causes a lot of hassle. And if you just multiply the number by 100 or anything appropriate for the currency unit you are using, it may make it easier for someone to accidentally type the wrong number. A fixed point type is what you would want. Fraction is a generalization to a fixed point type. On the other hand, if you are adding a lot of relatively small reals with unequal denominators together, you could easily get a denominator so large that wastes memory and makes the computation slow. Floating points would be necessary in this case.</p>
<p>So, whatever you choose, if people use your language a lot and use it for every task, they will want to also define the other by themselves. So it's a good idea to have them both. If you have to choose only one, notice that there is little to no performance loss if someone defines a rational type by themselves, but there is very much for a floating point type, because in this case it's very likely it would not be able to use the floating point instructions of the CPU.</p>

CC BY-SA 4.0
2023-07-03T10:11:32.880Z
2023-07-19T11:20:00.383Z
2023-07-19T11:20:00.383Z
user23013
<p>Fractions allow absolute precision with values where operations with floats would be only approximate. Even such innocently looking fractions like 0.1 are infinite in they binary representation as used by computer, so results are not precise. 0.1 decimal is 0.00011001100110011 ... binary.</p>
<p>While in many cases this rounding error is small and not worth to be cared about, there are algorithms and use cases where it accumulates over many iterations, making result totally wrong.</p>

CC BY-SA 4.0
2023-06-28T15:59:11.577Z
2023-06-28T15:59:11.577Z
Audrius Meškauskas
<h1><a href="https://en.wikipedia.org/wiki/Floating-point_arithmetic" rel="noreferrer">Floating-point</a> vs <a href="https://en.wikipedia.org/wiki/Rational_data_type" rel="noreferrer">rational</a> representation</h1>
<h2>Advantages of rational representation:</h2>
<ul>
<li><strong>Precision:</strong> rational numbers are exact. <code>1/7</code> prints as <code>1/7</code>. <code>1/7</code> in floating-point prints as <code>0.14285714285714285</code>. When doing complex arithmetic floating-point numbers will start to have minor offsets like <code>5.7000000000000152</code></li>
<li><strong>Consistency:</strong> because of precision, rational numbers don't suffer from <a href="https://stackoverflow.com/questions/588004/is-floating-point-math-broken">floating-point errors</a> <a href="https://docs.python.org/3/tutorial/floatingpoint.html" rel="noreferrer">[2]</a>, so they don't have issues like (<code>0.1 + 0.1 + 0.1 != 0.3</code>)</li>
<li><strong>Easier to understand:</strong> rational numbers are taught in elementary school, whereas many people don't understand <a href="https://floating-point-gui.de/formats/fp/" rel="noreferrer">how floating-point numbers actually work</a></li>
<li><strong>Easier to reason about mathematically:</strong> Because we have more literature on rational numbers than floating-points. Also, they're easier to reason about formally (e.g. prove theorems): when proving theorems about floating-point arithmetic, you have to handle its inconsistency, which makes the proofs much harder</li>
</ul>
<h2>Advantages of floating-point representation:</h2>
<ul>
<li><strong>Performance:</strong> Floating-point arithmetic is <em>much</em> faster than rational arithmetic, computers have built-in hardware and floats have been designed to be used in AAA games, neural network simulations, and other speed-critical processes. It also uses lower energy consumption</li>
<li><strong>Space:</strong> If you define rational numbers to take up bounded space (e.g. <code>i32 / i32</code>), you severely restrict the range of numbers that can be represented, and you essentially lose all of the advantages described above since you can no longer represent the results of certain arithmetic. So rational numbers are usually defined to take up unbounded space. But this means they almost always take up most space than floating-point, and depending on the computation, their space requirements increase <em>fast</em>.
<ul>
<li>Floating-points can represent extremely large and extremely small finite values in 64-bits (as large as <a href="https://stackoverflow.com/questions/29142914/double-precision-max-value"><code>1.8e308</code></a> and as small as <a href="https://stackoverflow.com/a/1217509"><code>4.9406564584124654e-324</code></a>), by skipping intermediate numbers (i.e. <code>1.8e308 - 1 == 1.8e308</code>). Rational numbers can also represent numbers of these size but they take up a lot of memory</li>
</ul>
</li>
<li><strong>Floating-point can approximate any real number:</strong> there's also <code>π</code>, <code>e</code>, &quot;sqrt(n)&quot; and other numbers which don't have <em>explicit</em> representations in floating point, but do have <em>approximate</em> ones. Approximating floating-point numbers is practically always ok because they are already inaccurate. But if you decide to approximate rational numbers, you lose the &quot;exact&quot; advantage, and must be careful on how you approximate to avoid losing the others.</li>
</ul>
<h2>Alternate number representations</h2>
<p>What if you want to exactly represent irrational numbers as well? You can extend your number definition to include not only rational numbers, but other constants and functions. Or, you can just directly encode mathematical formulas. The downside of this representation is that it's very complex and canonicalization varies from difficult to literally undecidable; you will have equivalent numbers that can't be proven equivalent, you will have numbers that you don't know are positive or negative, etc..</p>
<p>There are even more alternate representations. <a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic" rel="noreferrer">Fixed-point</a> lets you represent exact numbers like currency as integers by putting the decimal at an arbitrary position (e.g. <code>3.54</code> represented by the integer <code>354</code>, <code>400.62</code> represented by the integer <code>40062</code>). This representation is very similar to integers, and has most of their benefits and drawbacks which can be discussed in a separate question. Then there are esoteric representations like <a href="https://en.wikipedia.org/wiki/Logarithmic_number_system" rel="noreferrer">logarithmic</a>, <a href="https://en.wikipedia.org/wiki/Tapered_floating-point_representation" rel="noreferrer">tapered floating-point</a>, and <a href="https://en.wikipedia.org/wiki/Interval_arithmetic" rel="noreferrer">interval arithmetic</a>, many of which have specific use cases (for instance, intervals can be used to put a precise bound on how much a floating-point could be inaccurate, to maintain consistency as described above).</p>
<p>See <a href="https://en.wikipedia.org/wiki/Floating-point_arithmetic#Alternatives_to_floating-point_numbers" rel="noreferrer">here</a> for more info.</p>
<h2>How are they used in practice?</h2>
<p>Floating points are used <em>much</em> more than rational numbers. In fact, often when developers need precision, they figure out a way integers instead of decimals at all, such as in <a href="https://fangpenlin.com/posts/2019/10/07/elliptic-curve-cryptography-explained/#the-problem-of-floating-point" rel="noreferrer">Elliptic Curve Cryptography</a>. The space complexity and degraded performance are a big issue.</p>
<p>However, rational numbers are still very important, particularly in scientific computing where precision is important and performance is negotiable. Some languages have rational numbers in their standard library including <a href="https://en.cppreference.com/w/cpp/header/ratio" rel="noreferrer">C++</a>, <a href="https://clojure.org/reference/data_structures#Numbers" rel="noreferrer">Clojure</a>, <a href="https://docs.racket-lang.org/guide/numbers.html" rel="noreferrer">Racket</a> (in fact, most lisps), <a href="https://docs.python.org/3/library/fractions.html" rel="noreferrer">Python</a>, <a href="https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/" rel="noreferrer">Julia</a>, <a href="https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Ratio.html" rel="noreferrer">Haskell</a> <a href="https://en.wikipedia.org/wiki/Rational_data_type#Language_support" rel="noreferrer">(see more)</a></p>

CC BY-SA 4.0
2023-05-16T18:28:14.810Z
2023-05-16T19:06:26.953Z
2023-05-16T19:06:26.953Z
tarzh
CC BY-SA 4.0
2023-06-29T16:39:19.810Z
Another approach that a language I'm working on uses is storing numbers as infinite series of rationals. So you get the precision of rationals, the acceptable fuzziness of floats with irrational values, and...the performance of neither :p
rydwolf
CC BY-SA 4.0
2023-07-05T22:03:05.933Z
@RydwolfPrograms How does this compare to the Cauchy definition of real numbers?
James Wood
CC BY-SA 4.0
2023-07-06T01:10:51.480Z
@JamesWood Never heard of it. Looks like it's very similar though, so you've just given me a useful search term for finding prior work :p
rydwolf
CC BY-SA 4.0
2023-05-16T18:34:17.503Z
Another interesting alternative is the [floating bar](https://iquilezles.org/articles/floatingbar/) representation. Basically it is a fixed number of bits, but a few bits are used to control how many of the remaining bits are used for the numerator vs. the denominator.
kaya3
CC BY-SA 4.0
2023-05-16T18:40:47.707Z
Good answer! One minor nitpick: while it's true that not all real numbers are rational, that has nothing to do with +/- Infinity and NaN, which are not real numbers. (And it would be pretty easy to represent them in a rational number type as 1/0, -1/0, and 0/0.)
DLosc
# What are the advantages and disadvantages of implementing reals as rationals?
Posted by: username_0 on 2023-05-16
(By rationals, I mean fractions. So, `0.5` would be stored as `1/2`.)

There's only one language I know of which does this. So, what are the advantages and disadvantages of it?
**Tags:** numbers
> **Comments:**
> **username_4**: Do you understand what "floating point" means? Also, what language are you thinking of?  
> **username_0**: @SolomonUcko I thought it meant numbers which have a decimal place. Do you want me to change it to "real"? And I'm thinking of [Vyxal](https://vyxal.pythonanywhere.com/?v=1#WyIiLCIiLCIuNSIsIiIsIiJd).  
> **username_5**: Lots of language support both. Most Lisps I know use rationals and devolve into floats when you do something like `sqrt` that's inherently imprecise. Raku works similarly.  
> **username_2**: @TheThonnu, there is *fixed point*, which has the decimal point in a fixed position, and *floating point* where the point can be in any position. Business languages (e.g. COBOL) make good use of fixed point.  E.g. `COST` could be defined as a fixed point number with 2 digits after the decimal (suitable for storing money, without the round-off problems that floating-point values would have). Most other languages restrict all fixed point values to always having no digits after the decimal.  
> **username_6**: Do you mean reals are written in decimal but stored as rationals?  
> **username_0**: @alephalpha yes, in the program they are written like `.5`, but stored and output like `1/2`.  
> **username_1**: Every single CAS language does this, for very obvious reasons.  
> **username_2**: It would be interesting for a rational number's representation to include an extra 64 bits for a floating-point value, and have all operations perform both calculations. It's already big and slow, so it wouldn't make much difference to the performance, but it would make it easy to observe how much difference the two values have when the results are displayed. (e.g. for a specific application, confirming that the rational numbers *do* give a significantly better answer, or discovering that they really weren't needed.)  
> **username_1**: @RayButterworth if you want to see how much precision you're losing by using floating point, there is interval arithmetic for you. It can even be fast and hardware-assisted.  
> **username_3**: @TheThonnu: What is that one language?  ABC (a predecessor to Python)?  
> **username_0**: @dan04 it's [Vyxal](https://vyxal.pythonanywhere.com/?v=1#WyIiLCIiLCIuNSIsIiIsIiJd), a stack-based golfing language.  
---
## 7 answers
### Answer by username_21 on 2023-05-16. Score: 32 | Accepted answer
# [Floating-point](https://en.wikipedia.org/wiki/Floating-point_arithmetic) vs [rational](https://en.wikipedia.org/wiki/Rational_data_type) representation

## Advantages of rational representation:

* **Precision:** rational numbers are exact. `1/7` prints as `1/7`. `1/7` in floating-point prints as `0.14285714285714285`. When doing complex arithmetic floating-point numbers will start to have minor offsets like `5.7000000000000152`
* **Consistency:** because of precision, rational numbers don't suffer from [floating-point errors](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) [[2]](https://docs.python.org/3/tutorial/floatingpoint.html), so they don't have issues like (`0.1 + 0.1 + 0.1 != 0.3`)
* **Easier to understand:** rational numbers are taught in elementary school, whereas many people don't understand [how floating-point numbers actually work](https://floating-point-gui.de/formats/fp/)
* **Easier to reason about mathematically:** Because we have more literature on rational numbers than floating-points. Also, they're easier to reason about formally (e.g. prove theorems): when proving theorems about floating-point arithmetic, you have to handle its inconsistency, which makes the proofs much harder

## Advantages of floating-point representation:

* **Performance:** Floating-point arithmetic is *much* faster than rational arithmetic, computers have built-in hardware and floats have been designed to be used in AAA games, neural network simulations, and other speed-critical processes. It also uses lower energy consumption
* **Space:** If you define rational numbers to take up bounded space (e.g. `i32 / i32`), you severely restrict the range of numbers that can be represented, and you essentially lose all of the advantages described above since you can no longer represent the results of certain arithmetic. So rational numbers are usually defined to take up unbounded space. But this means they almost always take up most space than floating-point, and depending on the computation, their space requirements increase *fast*.
  + Floating-points can represent extremely large and extremely small finite values in 64-bits (as large as [`1.8e308`](https://stackoverflow.com/questions/29142914/double-precision-max-value) and as small as [`4.9406564584124654e-324`](https://stackoverflow.com/a/1217509)), by skipping intermediate numbers (i.e. `1.8e308 - 1 == 1.8e308`). Rational numbers can also represent numbers of these size but they take up a lot of memory
* **Floating-point can approximate any real number:** there's also `π`, `e`, "sqrt(n)" and other numbers which don't have *explicit* representations in floating point, but do have *approximate* ones. Approximating floating-point numbers is practically always ok because they are already inaccurate. But if you decide to approximate rational numbers, you lose the "exact" advantage, and must be careful on how you approximate to avoid losing the others.

## Alternate number representations

What if you want to exactly represent irrational numbers as well? You can extend your number definition to include not only rational numbers, but other constants and functions. Or, you can just directly encode mathematical formulas. The downside of this representation is that it's very complex and canonicalization varies from difficult to literally undecidable; you will have equivalent numbers that can't be proven equivalent, you will have numbers that you don't know are positive or negative, etc..

There are even more alternate representations. [Fixed-point](https://en.wikipedia.org/wiki/Fixed-point_arithmetic) lets you represent exact numbers like currency as integers by putting the decimal at an arbitrary position (e.g. `3.54` represented by the integer `354`, `400.62` represented by the integer `40062`). This representation is very similar to integers, and has most of their benefits and drawbacks which can be discussed in a separate question. Then there are esoteric representations like [logarithmic](https://en.wikipedia.org/wiki/Logarithmic_number_system), [tapered floating-point](https://en.wikipedia.org/wiki/Tapered_floating-point_representation), and [interval arithmetic](https://en.wikipedia.org/wiki/Interval_arithmetic), many of which have specific use cases (for instance, intervals can be used to put a precise bound on how much a floating-point could be inaccurate, to maintain consistency as described above).

See [here](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Alternatives_to_floating-point_numbers) for more info.

## How are they used in practice?

Floating points are used *much* more than rational numbers. In fact, often when developers need precision, they figure out a way integers instead of decimals at all, such as in [Elliptic Curve Cryptography](https://fangpenlin.com/posts/2019/10/07/elliptic-curve-cryptography-explained/#the-problem-of-floating-point). The space complexity and degraded performance are a big issue.

However, rational numbers are still very important, particularly in scientific computing where precision is important and performance is negotiable. Some languages have rational numbers in their standard library including [C++](https://en.cppreference.com/w/cpp/header/ratio), [Clojure](https://clojure.org/reference/data_structures#Numbers), [Racket](https://docs.racket-lang.org/guide/numbers.html) (in fact, most lisps), [Python](https://docs.python.org/3/library/fractions.html), [Julia](https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/), [Haskell](https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Ratio.html) [(see more)](https://en.wikipedia.org/wiki/Rational_data_type#Language_support)
> **Comments:**
> **username_15** (2023-05-16): Another interesting alternative is the [floating bar](https://iquilezles.org/articles/floatingbar/) representation. Basically it is a fixed number of bits, but a few bits are used to control how many of the remaining bits are used for the numerator vs. the denominator.  
> **username_24** (2023-05-16): Good answer! One minor nitpick: while it's true that not all real numbers are rational, that has nothing to do with +/- Infinity and NaN, which are not real numbers. (And it would be pretty easy to represent them in a rational number type as 1/0, -1/0, and 0/0.)  
> **username_22** (2023-06-29): Another approach that a language I'm working on uses is storing numbers as infinite series of rationals. So you get the precision of rationals, the acceptable fuzziness of floats with irrational values, and...the performance of neither :p  
> **username_23** (2023-07-05): @RydwolfPrograms How does this compare to the Cauchy definition of real numbers?  
> **username_22** (2023-07-06): @JamesWood Never heard of it. Looks like it's very similar though, so you've just given me a useful search term for finding prior work :p  
---
### Answer by username_11 on 2023-06-28. Score: 8
I am implementing a language that uses rationals by default. Let me tell you why.

The other answers are correct that performance and memory use are worse with rationals. My rationals are 32 bytes against an 8-byte, 64-bit floating point double.

And yet most uses of programming languages are for one-off scripts. The vast majority, in fact. In those cases, the programmer/user just wants to accomplish the task at hand and move on, not caring about performance because it's probably "good enough."

It's best if they don't have to worry about stupid issues like overflow, underflow, catastrophic cancellation, or any such type of bug.

So by default, my language uses rationals. In fact, users will also be able to attach units to numbers and will handle all conversions itself; the user just says the input units and the output units.

This will avoid bugs for the 99% case of a quick script when performance doesn't matter.

Of course, it will also have floating point, fixed-point, and standard integers. You'll also be able to attach units to those.

So yes, the cons the other answers listed exist. But the biggest pro of rationals, in my opinion, is that they help the user avoid bugs.
> **Comments:**
> **username_12** (2023-06-29): Are you aware of the [Frink](https://frinklang.org/) programming language? It sounds like there is some overlap regarding [numeric types](https://frinklang.org/#NumericTypes) (including [intervals](https://frinklang.org/#IntervalArithmetic)) and [units](https://frinklang.org/#HowFrinkIsDifferent) and [unit conversions](https://frinklang.org/#Conversions).  
> **username_11** (2023-06-29): Very aware. I was going to generate my units from Frink's incredibly awesome and downright snarky units file. The thing with Frink is that it focuses on being a `bc`-like language rather than a general-purpose one, so I'm making a more general-purpose one. And yes, I'll have intervals too.  
---
### Answer by username_3 on 2023-07-05. Score: 7
# Why not both?

That is, make your “real” type a variant that can store either an exact rational *or* an approximate floating point value.

You could make rational the “default”, in that literal `0.1` would represent the fraction 1/10. But `0.1f` would be the floating-point approximation 0xCCCCCCCCCCCCD × 2-55 = 0.1000000000000000055511151231257827021181583404541015625 (assuming IEEE 754 `double`).

The operations `+`, `-`, `*`, and `/` on rationals, plus `int / int` division, would return a rational. But any arithmetic involving floating-point, and any non-rationality preserving mathematical functions like trig or logarithms would return a floating-point value.

That way, you get exact arithmetic when you can, and approximate arithmetic when you need it. If rational arithmetic is hurting performance for a particular algorithm, a simple typecast to `float` or multiplication by `1.0f` will force floating-point arithmetic.
---
### Answer by username_7 on 2023-05-16. Score: 5
## Advantages

* Rationals can exactly represent a much wider range of numbers. One-third would be finitely representable.
* Floating point errors would be unheard of.

## Disadvantages

* It is computationally much more expensive to compute with arbitrary-sized rationals.
* If you do not reduce them, they may take up a lot more memory, even with the same number. `1/1` takes up a lot less space than `100000/100000`.
> **Comments:**
> **username_10** (2023-05-16): Worth stressing that there's not only a little but rather a huge gulf between the efficiency of computing with floats (with support built into the processor) and arbitrary-size rationals.  
> **username_8** (2023-06-28): Is the computational penalty just a legacy? *If* we had good reasons to always use rationals instead of floating-point, then processors would have built-in rational arithmetic, and rational arithmetic would be as fast as floating-point arithmetic, wouldn't it? Or would it still be prohibitively expensive to have to compute `gcd` too often to simplify fractions?  
> **username_3** (2023-07-05): @Stef: The technical difficulty with hardware rational support is that you'd need support for arbitrary-sized integers, which aren't suited to fixed-size registers and ALUs.  Unless you imposed a restriction like “numerator and denominator must each fit in 64 bits”, but then you'd lose exact arithmetic.  
> **username_9** (2023-07-19): For a given representation size, floating-point can represent _more_ numbers (and a bigger range), because every non-NaN float represents a distinct value.  There's holes in the rational space, because, e.g. ²⁄₄ == ¹⁄₂.  
---
### Answer by username_12 on 2023-06-28. Score: 3
> What are the advantages and disadvantages of implementing reals as rationals?

The biggest disadvantage is that it is not possible. There are [countably infinitely many](https://wikipedia.org/wiki/Countable_set) rationals, i.e., there are exactly as many rationals as there are natural numbers. However, there are [uncountably infinitely many](https://wikipedia.org/wiki/Uncountable_set) reals, i.e., there are strictly more reals than there are rationals (infinitely more, in fact).

Therefore, reals cannot be represented as rationals. This has been known for at least 2500 years when the irrationality of √2 was proven by the Pythagoreans in Ancient Greece.
> **Comments:**
> **username_13** (2023-06-28): Did you see the other answers that say it is possible? This is wrong.  
> **username_14** (2023-06-28): @Starship-OnStrike You cannot represent *all* real numbers on a computer. This is a direct result of the cardinality issues Jörg mentions. In particular, you cannot represent the uncomputable real numbers. These are particular real numbers that you cannot represent on a computer, regardless of how you try to represent them. See the answers [here](https://math.stackexchange.com/questions/462790/are-there-any-examples-of-non-computable-real-numbers), for example. Also [this](https://en.wikipedia.org/wiki/Computable_number#Properties)  
> **username_15** (2023-06-28): This argument applies equally against both rational and floating-point representations, and also every other representation. In fact it's worse for IEEE 754 floating-point representations, because they are limited to a *finite* number of representable numbers, whereas rational representations using bigints can at least represent countably many numbers (subject to memory availability).  
> **username_14** (2023-06-28): @kaya3-supportthestrike That is true, but it seems like this is important to mention those things when the question is "What are the advantages and disadvantages of implementing reals as ..." regardless of what follows the word "as," since the question is already not (fully) possible. This answer relates to the "implementing reals ..." part rather than the "... as rationals" part and both parts are important to the question, IMHO.  
> **username_15** (2023-06-28): @DavidYoung I think it is utterly unimportant. The question is about rational representations compared to other representations, not about whether reals should or shouldn't be represented by a data type at all. Even so, the fact that a representation isn't perfect doesn't mean that a representation isn't possible.  
> **username_16** (2023-07-18): I value the point that rational numbers fail to represent all real numbers in a way different from IEEE 754, but I also note that [Coq](https://coq.inria.fr/distrib/current/stdlib/Coq.Reals.Reals.html) and [Lean](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Real/Basic.html) have *real* reals.  
> **username_17** (2023-07-19): How long would it take to add together the first 70 elements of the harmonic series using rational numbers?  How about the first thousand?  Even a 1970s Apple II using interpreted BASIC could add the first 1000 terms using 40-bit loading-point values in under 10 seconds.  Could a modern computer perform that well with rational numbers?  
> **username_18** (2023-07-25): @Starship-OnStrike You can argue with mathematics, but you lose.  
---
### Answer by username_19 on 2023-07-03. Score: 3
There are no advantages and disadvantages, because in real programs, people will need both depending on the situation.

For example, if you are representing money, and using floats, it may make a number slightly off in some cases, and causes a lot of hassle. And if you just multiply the number by 100 or anything appropriate for the currency unit you are using, it may make it easier for someone to accidentally type the wrong number. A fixed point type is what you would want. Fraction is a generalization to a fixed point type. On the other hand, if you are adding a lot of relatively small reals with unequal denominators together, you could easily get a denominator so large that wastes memory and makes the computation slow. Floating points would be necessary in this case.

So, whatever you choose, if people use your language a lot and use it for every task, they will want to also define the other by themselves. So it's a good idea to have them both. If you have to choose only one, notice that there is little to no performance loss if someone defines a rational type by themselves, but there is very much for a floating point type, because in this case it's very likely it would not be able to use the floating point instructions of the CPU.
---
### Answer by username_20 on 2023-06-28. Score: 3
Fractions allow absolute precision with values where operations with floats would be only approximate. Even such innocently looking fractions like 0.1 are infinite in they binary representation as used by computer, so results are not precise. 0.1 decimal is 0.00011001100110011 ... binary.

While in many cases this rounding error is small and not worth to be cared about, there are algorithms and use cases where it accumulates over many iterations, making result totally wrong.
---
