Rational numbers are essential in computer science for representing precise values and performing accurate calculations in code, especially when dealing with fractions, ratios, or divisions.
What are rational numbers?
A rational number is any number that can be written as a fraction, where the numerator and the denominator are both integers and the denominator is not zero. The general form of a rational number is:
a / b, where:
a is an integer (… -3, -2, -1, 0, 1, 2, 3 …)
b is a non-zero integer
This means that rational numbers include all fractions (both positive and negative), all whole numbers (because they can be written as fractions), and all terminating and recurring decimals.
Notation for rational numbers
The set of all rational numbers is denoted by the letter ℚ. This symbol comes from the word quotient, because a rational number is the result of dividing one integer by another (as long as the divisor is not zero).
Examples of rational numbers:
4 (can be written as 4 / 1)
-7 (can be written as -7 / 1)
2/3
-5/9
0.25 (equals 1 / 4)
0.333… (equals 1 / 3)
These are all rational because they can be expressed in the form a / b.
Decimal forms of rational numbers
Rational numbers are either terminating decimals or recurring decimals.
Practice Questions
FAQ
Decimals that either terminate (come to an end) or repeat a pattern infinitely can be written as rational numbers because they can be expressed as the ratio of two integers. For example, 0.75 terminates and is equal to 3 divided by 4, while 0.333... repeats and is equal to 1 divided by 3. These decimals represent fractions where both the numerator and denominator are integers, fulfilling the definition of a rational number. On the other hand, non-terminating, non-repeating decimals such as pi or the square root of 2 cannot be written as a ratio of two integers. They have decimal expansions that go on forever without forming a predictable pattern. This unpredictability means they do not fit the definition of a rational number and are instead classified as irrational. Rational numbers have a structured, finite or repeating decimal form, while irrational numbers do not and therefore cannot be exactly expressed using fractions.
Computer systems do not natively represent rational numbers in most general-purpose programming environments. Instead, they typically use fixed-point or floating-point representations to store and compute with numbers, which can introduce rounding errors. To handle rational numbers precisely, some languages or libraries use data structures that store both the numerator and denominator as integers. These structures implement arithmetic operations (addition, subtraction, multiplication, division) by manipulating these integer components directly. For instance, Python’s Fraction class from the fractions module represents rational numbers exactly and handles simplification automatically. When you add two Fraction objects, the program finds a common denominator and performs integer operations to preserve accuracy. This avoids the floating-point errors found in binary representation. However, rational number arithmetic is slower and consumes more memory than floating-point due to storing and manipulating larger integers, especially when results are not simplified. So, although precise, rational representations are typically used only when absolute accuracy is required.
Yes, rational numbers are well-suited for representing percentages accurately in code, especially when precision is important and rounding errors must be avoided. A percentage is just a ratio expressed out of 100, which makes it inherently a rational number. For example, 75 percent can be represented as the rational number 75 divided by 100, which simplifies to 3 divided by 4. When calculations involving percentages are performed using floating-point numbers, they may suffer from binary approximation errors, particularly when converting between formats or doing repeated arithmetic. By using rational numbers, such as Python's Fraction(75, 100), the percentage is preserved exactly throughout all operations. This is useful in financial software, data visualisation, and statistical programs where even tiny errors in percentage calculations can lead to incorrect conclusions. Rational numbers maintain the integrity of percentage-based computations and ensure that the final results are both precise and mathematically accurate.
When two rational numbers are multiplied or divided in code using a rational number type, the operations are carried out exactly using the numerators and denominators of each operand. For multiplication, the numerators are multiplied together and the denominators are multiplied together. For example, (2/3) (4/5) results in (2 4) / (3 5) = 8/15. For division, the first rational number is multiplied by the reciprocal (or inverse) of the second. So (2/3) ÷ (4/5) becomes (2/3) (5/4) = 10/12, which is then simplified to 5/6. In languages like Python, the Fraction class performs this simplification automatically, ensuring that the result remains in its lowest terms. These operations avoid the loss of precision typical in floating-point arithmetic and produce exact results. This behaviour is vital in domains such as symbolic computation and algebraic modelling, where exact arithmetic is a foundational requirement.
Yes, while rational numbers provide exact results and are beneficial in applications requiring high precision, they do come with some storage and performance trade-offs. Unlike floating-point numbers, which use a fixed-size binary representation (typically 32-bit or 64-bit), rational numbers must store both the numerator and denominator, which are integers that can grow arbitrarily large. As a result, operations on rational numbers can be slower, especially when the values become large or require frequent simplification. Multiplication, division, and simplification involve multiple steps, such as computing the greatest common divisor (GCD), which can increase computational overhead. This is particularly noticeable in performance-critical applications such as video games, embedded systems, or high-speed numerical simulations where efficiency is more important than precision. For these reasons, floating-point arithmetic is often used in performance-sensitive contexts, while rational numbers are preferred in cases where exactness is non-negotiable. Developers must carefully consider these trade-offs when deciding how to represent numerical data.
