Search code examples
pythonfloating-pointfractions

Accurate Fractions with Python


I'm not seeing the mathematical results I'm expecting from the following code, which I believe should produce the Harmonic Series:

from fractions import Fraction

def sum_fracs(n):
    if n == 1:
        return 1
    return 1/n + sum_fracs(n - 1)

for n in range(1, 6):
    print(sum_fracs(n).as_integer_ratio())
    
for n in range(1, 6):
    print(Fraction(sum_fracs(n)))

Output:

(1, 1)
(3, 2)
(8256599316845909, 4503599627370496)
(2345624805922133, 1125899906842624)
(1285402393645329, 562949953421312)
1
3/2
8256599316845909/4503599627370496
2345624805922133/1125899906842624
1285402393645329/562949953421312

Neither approach gives

1   

3/2

11/6    

25/12   

137/60

as I was hoping. I know floats can have rounding errors, but I would hope that something this basic would be possible in Python.

Any help much appreciated.


Solution

  • You run Fraction(x) where x is a float. This is too late, you already lost precision, so your fraction's precision is as good as that of the float.

    Use Fraction in the function:

    def sum_fracs(n):
        if n == 1:
            return 1
        return Fraction(1, n) + sum_fracs(n - 1)
    
    for n in range(1, 6):
        print(sum_fracs(n).as_integer_ratio())
    

    output:

    (1, 1)
    (3, 2)
    (11, 6)
    (25, 12)
    (137, 60)
    

    NB. this is clearly stated in the fraction documentation

    Note that due to the usual issues with binary floating-point (see Floating Point Arithmetic: Issues and Limitations), the argument to Fraction(1.1) is not exactly equal to 11/10, and so Fraction(1.1) does not return Fraction(11, 10) as one might expect. (But see the documentation for the limit_denominator() method below.)