Skip to content

Instantly share code, notes, and snippets.

@fidelsoto
Last active October 17, 2024 05:35
Show Gist options
  • Save fidelsoto/b4c0f14b800c58e137ad5757f35cacd6 to your computer and use it in GitHub Desktop.
Save fidelsoto/b4c0f14b800c58e137ad5757f35cacd6 to your computer and use it in GitHub Desktop.
Fraction Class C#. Supports formatting, comparing and simplifying fractions. In this code we can see the usage of constructors, operator overriding and helper functions.
public class Fraction
{
public int numerator;
public int denominator;
public Fraction(int numerator, int denominator)
{
this.numerator = numerator;
this.denominator = denominator;
}
public Fraction(Fraction fraction)
{
numerator = fraction.numerator;
denominator = fraction.denominator;
}
public override bool Equals(object obj)
{
Fraction other = obj as Fraction;
return (numerator == other.numerator && denominator == other.denominator);
}
public static bool operator ==(Fraction f1, Fraction f2)
{
return f1.Equals(f2);
}
public static bool operator !=(Fraction f1, Fraction f2)
{
return !(f1 == f2);
}
public override int GetHashCode()
{
return numerator * denominator;
}
public override string ToString()
{
return numerator + "/" + denominator;
}
//Helper function, simplifies a fraction.
public Fraction Simplify()
{
for (int divideBy = denominator; divideBy > 0; divideBy--)
{
bool divisible = true;
if ((int)(numerator / divideBy) * divideBy != numerator)
{
divisible = false;
}
else if ((int)(denominator / divideBy) * divideBy != denominator)
{
divisible = false;
}
else if (divisible)
{
numerator /= divideBy;
denominator /= divideBy;
}
}
return this;
}
}
@bonimy
Copy link

bonimy commented Aug 1, 2024

Here's an even better version. Uses integral types as it should, handles overflow better, implements all the interfaces it should, has a better simplification algorithm, and has methods with friendly names so other languages without operator overloading can still use the class.

public struct Fraction :
    IEquatable<Fraction>,
    IComparable<Fraction>,
    IFormattable
{
    public int Numerator;
    public int Denominator;

    public Fraction(int numerator, int denominator = 1)
    {
        Numerator = numerator;
        Denominator = denominator;
    }

    public static implicit operator Fraction(int value)
    {
        return new Fraction(value);
    }

    public static explicit operator double(Fraction value)
    {
        return (double)value.Numerator / value.Denominator;
    }

    public static bool operator ==(Fraction f1, Fraction f2)
    {
        return f1.Equals(f2);
    }

    public static bool operator !=(Fraction f1, Fraction f2)
    {
        return !(f1 == f2);
    }

    public static bool operator <(Fraction f1, Fraction f2)
    {
        return f1.CompareTo(f2) < 0;
    }

    public static bool operator >(Fraction f1, Fraction f2)
    {
        return f1.CompareTo(f2) > 0;
    }

    public static bool operator <=(Fraction f1, Fraction f2)
    {
        return f1.CompareTo(f2) <= 0;
    }

    public static bool operator >=(Fraction f1, Fraction f2)
    {
        return f1.CompareTo(f2) >= 0;
    }

    public readonly Fraction Reciprocal()
    {
        return new Fraction(Denominator, Numerator);
    }

    public readonly int CompareTo(Fraction other)
    {
        return ((long)Numerator * other.Denominator).CompareTo(
            (long)Denominator * other.Numerator);
    }

    public readonly bool Equals(Fraction other)
    {
        return CompareTo(other) == 0;
    }

    public override readonly bool Equals(object? obj)
    {
        return obj is Fraction other && Equals(other);
    }

    public static Fraction operator +(Fraction f)
    {
        return f;
    }

    public static Fraction operator -(Fraction f)
    {
        return new Fraction(-f.Numerator, f.Denominator);
    }

    public static Fraction operator +(Fraction f1, Fraction f2)
    {
        var numerator = ((long)f1.Numerator * f2.Denominator)
            + ((long)f1.Denominator * f2.Numerator);
        var denominator =(long)f1.Denominator * f2.Denominator;
        return FromLongsInternal(numerator, denominator);
    }

    public static Fraction operator -(Fraction f1, Fraction f2)
    {
        return f1 + (-f2);
    }

    public static Fraction operator *(Fraction f1, Fraction f2)
    {
        var numerator = (long)f1.Numerator * f2.Numerator;
        var denominator = (long)f1.Denominator * f2.Denominator;
        return FromLongsInternal(numerator, denominator);
    }

    public static Fraction operator /(Fraction f1, Fraction f2)
    {
        return f1 * f2.Reciprocal();
    }

    public readonly Fraction Add(Fraction other)
    {
        return this + other;
    }

    public readonly Fraction Subtract(Fraction other)
    {
        return this - other;
    }

    public readonly Fraction Multiply(Fraction other)
    {
        return this * other;
    }

    public readonly Fraction Divide(Fraction other)
    {
        return this / other;
    }

    public readonly override int GetHashCode()
    {
        return HashCode.Combine(Numerator, Denominator);
    }

    public readonly override string ToString()
    {
        return ToString(null, null);
    }

    public readonly string ToString(IFormatProvider? provider)
    {
        return ToString(null , provider);
    }

    public readonly string ToString(
        string? format,
        IFormatProvider? provider = null)
    {
        return new StringBuilder()
            .Append(Numerator.ToString(format, provider))
            .Append('/')
            .Append(Denominator.ToString(format, provider))
            .ToString();
    }

    public readonly Fraction Simplified()
    {
        if (Numerator == 0)
        {
            return 0;
        }

        var result = this / (int)GCD(Numerator, Denominator);
        if (result.Denominator < 0)
        {
            result.Numerator = -result.Numerator;
            result.Denominator = -result.Denominator;
        }

        return result;
    }

    private static Fraction FromLongsInternal(long numerator, long denominator)
    {
        // Only gets called during arithmetic operations for simplification
        // guarantees.

        if (numerator == 0)
        {
            return 0;
        }

        var gcd = GCD(numerator, denominator);

        if (denominator < 0)
        {
            numerator = -numerator;
            denominator = -denominator;
        }

        return new Fraction((int)(numerator / gcd), (int)(denominator / gcd));
    }

    private static long GCD(long a, long b)
    {
        if (a < 0)
        {
            a = -a;
        }

        if (b < 0)
        {
            b = -b;
        }

        while (a != 0 && b != 0)
        {
            if (a > b)
            {
                a %= b;
            }
            else
            {
                b %= a;
            }
        }

        return a | b;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment