Reputation: 103
I'm trying to take advantage of Python's nearly infinite integer precision in order to work with large fractions, but there seem to be some precision issues creeping in anyway.
I have a class called Fraction, which has a numerator attribute and a denominator attribute. I then defined the arithmetic operations on the class using only addition/subtraction and multiplication of integer components. The idea is that there will never be any precision loss due to floating point issues. The initializer method and subtraction method are shown below
class Fraction:
def __init__(self, numerator, denominator = 1):
self.numerator = numerator/GCD(numerator,denominator)
self.denominator = denominator/GCD(numerator,denominator)
def __sub__(self,other):
print("x:",int(self.denominator))
print("y:",int(other.numerator))
print("x*y:",int(self.denominator*other.numerator))
return Fraction(self.numerator*other.denominator - self.denominator*other.numerator, self.denominator*other.denominator)
The print
statements were added for debugging purposes, and they highlight the issue.
When I run the program in python3.6.1, the following issues develop when I start performing operations in the terminal:
Python Shell Input:
Fraction(1,29) - Fraction(7924325834608857,229805449203656864)
Output:
x: 29
y: 7924325834608857
x*y: 229805449203656864
(0/1)
The problem is that 29*7924325834608857
does not equal 229805449203656864
So what's going on?
EDIT in response to comments: Here is the GCD function I used. It has worked for every test case I tried it on, but maybe the precision issue is with python's modulus operator?
def GCD(num1,num2):
a = max(num1,num2)
b = min(num1,num2)
if a == 0 and b != 0:
return b
elif b == 0 and a !=0:
return a
elif a%b == 0:
return b
else:
return GCD(a%b, b)
Upvotes: 0
Views: 189
Reputation: 1884
The problem with the incorrect calculation appears to be a float rounding issue. Because in __init__
, you have a division operation, which returns a float, so the following calculations work with floats as well, and there, rounding issues might appear. You do cast to int in __sub__
, but after the calculation took place. To test, try:
29 * 7924325834608857.0
29 * 7924325834608857
29 * 7924325834608857.0 == 29 * 7924325834608857
Results will be
2.2980544920365686e+17
229805449203656853
False
To fix this, you could make a floor division (Which ignores the remainder of a division, making the result an int) in __init__
, like this
class Fraction():
def __init__(self, numerator, denominator=1):
self.numerator = numerator // GCD(numerator, denominator)
self.denominator = denominator // GCD(numerator, denominator)
Upvotes: 2