Fraction Type

A fraction is a rational number object. It keeps both, a numerator and a denominator, to avoid some of the inaccuracies and limitations of floating-point math.

Fraction basics

The Fraction resides in a module named fractions which must be imported.

>>> from fractions import Fraction
>>> x = Fraction(1, 5)
>>> y = Fraction(2, 7)
>>> 
>>> x
Fraction(1, 5)
>>> y
Fraction(2, 7)

Fractions can be used in mathematical expressions.

>>> x + y
Fraction(17, 35)
>>> x - y
Fraction(-3, 35)
>>> x * y
Fraction(2, 35)
>>> x / y
Fraction(7, 10)
>>> print(x / y)
7/10
>>> print(x)
1/5

Fraction objects can be created from floating-point number strings.

>>> Fraction('.35')
Fraction(7, 20)
>>> a = Fraction('.68')
>>> a
Fraction(17, 25)
>>> print(a)
17/25

Numeric accuracy in fractions and decimals

Operations run with floating-point objects; they may display fewer digits in recent Pythons than they used to, but they still aren’t exact values in memory:

>>> a = 1 / 3.0
>>> b = 4 / 6.0
>>> a
0.3333333333333333
>>> b
0.6666666666666666
>>> 
>>> a + b
1.0
>>> a - b
-0.3333333333333333
>>> a * b
0.2222222222222222

This floating-point limitation is especially apparent for values that cannot be represented accurately given their limited number of bits in memory. Both Fraction and Decimal provide ways to get exact results, but at the cost of some speed and code verbosity.

# floating-point numbers do not accurately give the zero answer expected
# but both of the other types do
>>> 0.1 + 0.1 + 0.1 - 0.3
5.551115123125783e-17
>>> 
>>> from fractions import Fraction
>>> Fraction(1, 10) + Fraction(1, 10) + Fraction(1, 10) - Fraction(3, 10)
Fraction(0, 1)
>>> 0 / 1
0.0
>>> 
>>> from decimal import Decimal
>>> Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3')
Decimal('0.0')

Fractions retain accuracy and simplify results.

>>> (1 / 3) + (2 / 5) - (8 / 5)
-0.8666666666666667
>>> 
>>> Fraction(24, 72)
Fraction(1, 3)
>>> 
>>> decimal.Decimal(str(1 / 3)) + decimal.Decimal(str(2 / 5)) - decimal.Decimal(str(8 / 5))
Decimal('-0.8666666666666667')

Fraction conversions and mixed types

Floating-point objects have a method that yields their numerator and denominator ratio.
Fractions have a from_float method, and float accepts a Fraction as an argument.

Note:
The * in the code below is special syntax that expands a tuple into individual arguments.

>>> from fractions import Fraction
>>>
>>> # float object method
>>> (2.5).as_integer_ratio()
(5, 2)
>>>
>>> # Convert float to fraction (first way)
>>> z = Fraction(*f.as_integer_ratio())
>>> z
Fraction(5, 2)
>>>
>>> x = Fraction('0.33')
>>> x
Fraction(33, 100)
>>> x + z
Fraction(283, 100)
>>> 
>>> # Convert fraction to float
>>> float(x)
0.33
>>> float(z)
2.5
>>> float(x + z)
2.83
>>> 283 / 100
2.83
>>>
>>> # Convert float to fraction (second way) 
>>> Fraction.from_float(2.75)
Fraction(11, 4)
>>> Fraction(*(2.75).as_integer_ratio())
Fraction(11, 4)

Type mixing is allowed in expressions.

>>> x
Fraction(33, 100)
>>> x + 2
Fraction(233, 100)
>>> x + 2.0
2.33
>>> x + (1 / 3)
0.6633333333333333
>>> x + (1.0 / 3)
0.6633333333333333
>>> x + (4 / 3)
1.6633333333333333
>>> x + Fraction(4, 3)
Fraction(499, 300)

Leave a Reply