Search code examples
pythonpython-3.xfunctionclassfractions

Fractions code not able to calculate difference for fractions with same values


I have created a fraction calculator which includes +,-,*,/. All my functions work fine except one condition in subtraction where both the values are equal. I believe it has to do with the simplify(gcd) function that I added but not sure where the problem is. Below is the code where I have kept only the subtraction function for prioritizing the problem:

Class Fraction:
     def __init__(self, num, denom) -> None:  

        self.num = int(num / simplify(abs(num), abs(denom)))
        self.denom = int(denom / simplify(abs(num), abs(denom)))
        if self.denom < 0:
            self.denom = abs(self.denom)
            self.num = -1*self.num
        elif self.denom == 0:
            raise ZeroDivisionError("cansnot divide by zero")


    def __sub__(self, other: "Fraction") -> "Fraction":  

    
        n: int = self.num * other.denom - self.denom * other.num
        d: int = self.denom * other.denom
        new = Fraction(n, d)
        return (new)
 
    def __str__(self) -> str:  

    
        if self.denom == 1:
            return str(self.num)
        else:
            return(f"{self.num}/{self.denom}")


def simplify(num, denom):

    while num != denom:
        if num > denom:
            num = num - denom
        else:
            denom = denom - num
    return num


def get_fraction() -> Fraction:

"""Convert numerator and denominator to integer values"""

while True:
    num: str = input("Enter the numerator")
    denom: str = input("Enter the denominator")
    try:
        a = int(num)
        b = int(denom)
        return Fraction(a, b)
    except ValueError:
        print("Enter valid numerator and denominator")

def compute(f1: Fraction, operator: str, f2: Fraction) -> None:

okay = True
if operator == '-':
    result = f1.__sub__(f2)
else:
    print(operator, " is an unrecognized operator")
    okay = False

if okay == True:
 
    print(f"{f1} {operator} {f2} = {result}")

def main() -> None:

"""Main menu"""

print("Welcome to the Fraction Calculator! ")
while True:
    print('Press Q to quit or any other key to start')
    z = input("Start or Quit?")
    if z == 'Q' or z == 'q':
        exit()
    else:
        f1: Fraction = get_fraction()
        operator: str = input("Operation (+, -, *, / , = , < , > , <= , >= , != ): ")
        f2: Fraction = get_fraction()

        try:
            compute(f1, operator, f2)
        except ZeroDivisionError as e:
            print(e)


if __name__ == '__main__':
    
    main()

Below is the output for this code, where I am having 4/5 as both the fractions, it just gives me a blank output.

Welcome to the Fraction Calculator!
Press Q to quit or any other key to start
Start or Quit? s
Enter the numerator 4
Enter the denominator 5
Operation (+, -, *, / , = , < , > , <= , >= , != ): -
Enter the numerator 4 
Enter the denominator 5

All other values work fine for the sub-function except when the values are equal.


Solution

  • Your simplify method is a slower version of a gcd and will fall into an infinite loop when num is zero.

    you could replace it with this:

     from math import gcd
     def simplify(num, denom):
         return gcd(num,denom) if denom else 1
    

    if you don't want to import math, you can write your own version of the gcd() function using a faster algorithm than the subtractive form you're currently using in simplify:

    def gcd(a,b):
        while b != 0: a,b = b,a%b
        return abs(a)         
    

    Or, you could replace simplify()'s code with an actual simplification of both components that places the sign on top in one step:

     def simplify(num, denom):
         g = gcd(num,denom) if denom else 1
         s = -1 if (num<0) != (denom<0) else 1
         return int(abs(num) // g * s), int(abs(denom) // g) 
    

    and use it like this in the constructor:

     self.num,self.denom = simplify(num,denom)
    

    if you convert the string input using Decimal() (from the decimal module) instead of int(), this improved simplify() could do even cooler things such as figuring out that:

    simplify(-8.5,12.5)   # is (-17, 25)
    

    For example:

    from decimal import Decimal
    f = input("enter a fraction as a/b or a decimal value: ")
    num,denom,*_ = map(Decimal,(*f.split("/"),1))
    print(simplify(num,denom))
    
    enter a fraction as a/b or a decimal value: 0.68
    (17, 25)