7.4. Working with Rational Numbers#

While floating-point numbers are powerful, they can’t represent fractions like 1/3 perfectly. For situations where exact ratios of integers are required, Julia has a dedicated rational number type. You can create a rational number using the // operator.

# The // operator creates a Rational{Int64} object.
# 355/113 is a well-known, highly accurate approximation of π.
a = 355 // 113
355//113

If you need to convert a rational number to an inexact floating-point representation, you can do so by calling the desired type as a function.

# This explicitly converts the exact fraction to an approximate Float64.
Float64(a)
3.1415929203539825

7.4.1. Automatic Simplification#

A key feature of rational numbers is that they are always stored in their canonical form: the numerator and denominator are reduced to their lowest terms, and the denominator is always positive.

# 6//9 is automatically reduced to its simplest form.
6//9
2//3
# The sign is always stored in the numerator.
5//-15
-1//3
# Double negatives are also simplified.
-4//-12
1//3

7.4.2. Arithmetic and Comparisons#

Because rational numbers are always in a canonical form, arithmetic and comparisons are exact and reliable. You can work with them without worrying about floating-point inaccuracies.

# Comparison works because both sides are reduced to 2//3 before checking equality.
2//3 == 6//9
true
3//7 < 1//2
true
# Arithmetic operations find a common denominator and return a new rational number.
2//4 + 1//6
2//3
5//8 * 3//12
5//32
6//5 / 10//7
21//25

7.4.3. Promotion with Other Numeric Types#

When you combine a rational number with another numeric type, Julia promotes the result to an appropriate type to preserve accuracy whenever possible.

# Rational + Integer -> Rational
3//5 + 1
8//5
# Rational + Float64 -> Float64. The exactness of the rational is lost.
3//5 - 0.5
0.09999999999999998
# Rational * Complex{Int} -> Complex{Rational{Int}}
2//7 * (1 + 2im)
2//7 + 4//7*im

This promotion system is why 0.5 == 1//2 is true, but comparisons with non-exact floating-point numbers fail.

# This is true because 0.5 can be perfectly represented in binary.
0.5 == 1//2
true
# This is false because the literal 0.33 is not the same as the exact fraction 1/3.
0.33 == 1//3
false
# We can see the small difference that results from the Float64's imprecision.
1//3 - 0.33
0.0033333333333332993