Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if a python fraction can have an equivalent decimal

Tags:

python

math

sympy

I need to write a python function that takes, as an argument, an object of class "Fraction", and determines whether the fraction can be represented in decimal format. For example, 1/2 can be represented by 0.5, but 1/3 has no decimal equivalent with finite number of digits (0.333333333 is an approximated value).

My approach was to compare the fraction with the numerator divided by the denominator as follows (assume that "frac" is the Fraction object):

 if frac == frac.numerator / frac.denominator:
      print('has decimal representaion')
 else:
      print('has no decimal representation')

but this doesn't work in many cases. For example Python mistakes the comparison Fraction(347, 1000) == 0.347, it returns False although it should be True. I know that python has a problem related to floating point operations, therefor I am looking for a workaround or a package that solves this problem.

Note: I used sympy but in sympy the comparison S(1)/3 == 1/3 returns True, where I need it to be False.

like image 283
Wessam Sharaf Avatar asked Feb 02 '17 08:02

Wessam Sharaf


2 Answers

Using this:

If the denominator is has any prime factors other than 2 and 5, it doesn't have an exact decimal representation. – khelwood

You could test this by dividing out the 2s and then 5s and then checking if the result is 1:

def has_decimal_representaion(frac):
    d = frac.denominator
    for n in (2, 5):
        while d % n == 0:
            d = d / n
    return d == 1
like image 51
Dan D. Avatar answered Sep 30 '22 17:09

Dan D.


The problem with checking Fraction(1, 3) == float(1/3) is that float(1/3) is also technically a rational number, since it's a finitely represented floating point number. To be sure that rational number is 6004799503160661/18014398509481984:

>>> (1/3).as_integer_ratio()
(6004799503160661, 18014398509481984)

This method would only work if computers could store infinitely many digits for a floating point number, which is impossible.

As for the easier test suggested by khelwood, if the denominator has factors other than 2 or 5, you can use SymPy's factorint with the limit argument to prevent it from trying to find large prime factors. Note that fraction.Fraction and sympy.Rational objects are always reduced to lowest terms automatically, so you don't need to worry about that.

def is_representable(rational):
    d = sympy.Rational(rational).q
    return not bool(set(sympy.factorint(d, limit=10).keys()) - {2, 5})
like image 45
asmeurer Avatar answered Sep 30 '22 17:09

asmeurer