Click here to Skip to main content
15,886,519 members
Articles / Programming Languages / C#
Article

Fractions in C#

Rate me:
Please Sign up or sign in to vote.
3.71/5 (6 votes)
17 Oct 20053 min read 114.2K   1.9K   21   13
An article on a Fraction class in C#.

Sample Image

Introduction

One of the important features that is missing from the .NET Framework is a Fraction class. Explanation that I got from The BCL Team is that "Because we have types which can represent the same data, we didn’t expose this type specifically. This is more useful for specific math related functionality. I’ve added a feature request for us to consider adding this in the next version of the product".

Until class Fraction (or maybe even built-in numeric type 'fraction') becomes part of the .NET Framework, we are on our own. This is not too much of a problem, because object-oriented programming languages allow us to create new data types. Unlike Java, C# supports operator overloading, so we can build a Fraction class that works very much like the built-in numeric types. This will also be a classic example to demonstrate the justified use of the operator overloading feature of C#.

Background

Fractions, also known as rationals, are numbers that can be expressed as a ratio of whole numbers. The top number is called the numerator, and the bottom number is called the denominator. They can be used to represent decimal numbers without loss of accuracy that is inherent to float and double types.

There are two types of fractions:

  • Simple Fractions (the numerator and the denominator are integers)
  • Complex Fractions (fractions where the numerator, denominator, or both contain a fraction)

Constructors

A numerator is allowed to take on the value of zero in a fraction. Any legal fraction (denominator not equal to zero) with a numerator equal to zero has an overall value of zero.

C#
public Fraction()
{
    Initialize( 0, 1 );
}

You can express an integer as a fraction by simply dividing by 1, or you can express any integer as a fraction by simply choosing a numerator and denominator so that the overall value is equal to the integer.

C#
public Fraction( int num )
{
  CheckMinValue( num );
  Initialize( num, 1 );
}

The denominator of any fraction cannot have the value zero. If the denominator of a fraction is zero, the expression is not a legal fraction because its overall value is undefined. In that case, ArithmeticException is thrown from the constructor.

C#
public Fraction( int num, int den )
{
  CheckDenominatorZero( den );

  CheckMinValue( num );
  CheckMinValue( den );

  Fraction f = new Fraction( (decimal)num, (decimal)den );
  Initialize( f.num, f.den );
}

Besides from constructors, instances of the Fraction class can also be made from string. Static method "Parse" accepts a simple or a complex fraction as input parameter and returns an instance of a Fraction (or throws exception). If there is an even number of minus signs in a fraction, the value of the fraction is positive. If there is an odd number of minus signs, the value of the fraction is negative.

C#
// throws FormatException if wrong fraction format
// throws OverflowException if reduced fraction does not fit in fraction range
// throws ArithmeticException if denominator is zero
public static Fraction Parse( string fraction )
{
    if ( fraction == null )
        throw new FormatException();

    string[] split = fraction.Split( '/' );
    int len = split.Length;

    if ( len == 2 )
    {
        int s0 = int.Parse( split[0] );
        int s1 = int.Parse( split[1] );
        return new Fraction( s0, s1 );
    }
    else if ( len == 4 )
    {
        int s0 = int.Parse( split[0] );
        int s1 = int.Parse( split[1] );
        Fraction f1 = new Fraction( s0, s1 );

        int s2 = int.Parse( split[2] );
        int s3 = int.Parse( split[3] );
        Fraction f2 = new Fraction( s2, s3 );

        return f1 / f2;
    }
    else
        throw new FormatException();
}

Addition

To add fractions, the denominators must be equal. The following steps are required to add two fractions.

  1. Build each fraction so that both denominators are equal.
  2. Add the numerators of the fractions.
  3. The denominators will be the denominator of the built-up fractions.
  4. Reduce the answer.
C#
public static Fraction operator + ( Fraction a, Fraction b )
{
    decimal r1 = (decimal)a.num * b.den + (decimal)b.num * a.den;
    decimal r2 = (decimal)a.den * b.den;
    return new Fraction( r1, r2 );
}

Subtraction

Subtraction is very similar to addition, the only difference is in step 2, where numerators should be subtracted.

C#
public static Fraction operator - ( Fraction a, Fraction b )
{
    decimal r1 = (decimal)a.num * b.den - (decimal)b.num * a.den;
    decimal r2 = (decimal)a.den * b.den;
    return new Fraction( r1, r2 );
}

Multiplication

To multiply two simple fractions, complete the following steps:

  1. Multiply the numerators.
  2. Multiply the denominators.
  3. Reduce the results.
C#
public static Fraction operator * ( Fraction a, Fraction b )
{
    decimal r1 = (decimal)a.num * b.num;
    decimal r2 = (decimal)a.den * b.den;
    return new Fraction( r1, r2 );
}

Division

To divide one fraction by a second fraction, convert the problem to multiplication and multiply the two fractions.

C#
public static Fraction operator / ( Fraction a, Fraction b )
{
    decimal r1 = (decimal)a.num * b.den;
    decimal r2 = (decimal)a.den * b.num;

    if ( r2 == 0 )
        throw new DivideByZeroException();
    else
        return new Fraction( r1, r2 );
}

Comparison operators

The Fraction class also includes six boolean comparison operators, which all have a return type of bool.

C#
public static bool operator == ( Fraction a, Fraction b )
{
    return (decimal)a.num * b.den == (decimal)b.num * a.den;
}

public static bool operator != ( Fraction a, Fraction b )
{
    return ( !( a == b ) );
}

public static bool operator > ( Fraction a, Fraction b )
{
    return (decimal)a.num * b.den > (decimal)b.num * a.den;
}

public static bool operator >= ( Fraction a, Fraction b )
{
    return (!( a < b ));
}

public static bool operator < ( Fraction a, Fraction b )
{
    return (decimal)a.num * b.den < (decimal)b.num * a.den;
}

public static bool operator <= ( Fraction a, Fraction b )
{
    return (!( a > b ));
}

Miscellaneous methods

C#
public override string ToString()
{
    if ( this.den == 1 )
        return this.num.ToString();
    else
        return this.num+"/"+this.den;
}

public override bool Equals( object o )
{
    if ( o == null || o.GetType() != GetType() )
        return false;
    Fraction f = (Fraction)o;
    return ( this == f );
}

public override int GetHashCode()
{
    return (int)( this.num ^ this.den );
}

public static implicit operator double( Fraction f )
{
    return (double)f.num / f.den;
}

public Fraction Inverse()
{
    return new Fraction( this.den, this.num );
}

Notes

  • Numerator and denominator must be in [-int.MaxValue, int.MaxValue].
  • Class is immutable
  • Fractions are auto-reducing
  • Binary operator overloading methods can throw overflow exception
  • All occurrences of type 'decimal' in class can be replaced with 'long' if compiled with /checked option.
  • The class is not optimized for speed

Future

With upcoming generics (and maybe BigInteger) support in C# 2.0, the class Fraction could be modified to something like class Fraction<T> { ... }, where T could be any integer type, decimal or BigInteger. In that case, all occurrences of type 'decimal' in current source should be replaced with BigInteger and 'int' should be replaced with T.

Resources

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer
Croatia Croatia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
JBoada7-Aug-13 8:37
JBoada7-Aug-13 8:37 
QuestionDecimal number to fraction Pin
Jajnick8-May-12 9:17
Jajnick8-May-12 9:17 
Questionparsing improvement Pin
Solarin8121-Jul-11 5:08
Solarin8121-Jul-11 5:08 
GeneralSome comments Pin
Drew Noakes25-Oct-05 0:02
Drew Noakes25-Oct-05 0:02 
GeneralRe: Some comments Pin
Nikola Stepan25-Oct-05 8:05
Nikola Stepan25-Oct-05 8:05 
GeneralRe: Some comments Pin
Kris Vandermotten26-Oct-05 10:10
Kris Vandermotten26-Oct-05 10:10 
Nikola Stepan wrote:
Structs cannot contain explicit parameterless constructors and struct members are automatically initialized to their default values. With 'struct' instead of 'class' new Fraction() will return illegal fraction.

Indeed, structs cannot have explicit parameterless constructors (they do have them implicitely) and struct fields are automatically initialized to default values when the default constructor is "called". But that does not imply that a struct rational would be initialized to 0/0. You could have a Denominator property, that returns denominator + 1 where denominator is a field (initialized to zero).

Also, you say 0/0 is "illegal", but that depends on the laws you create. Consider the double type, where 0.0/0.0 is not illegal, it's equal to double.NaN.

Oh, and by the way: as rational, 1/2 == 2/4, but as fractions 1/2 != 2/4. At least according to the vocabulary my teachers used.


GeneralRe: Some comments Pin
Nick Alexeev11-Oct-14 16:28
professionalNick Alexeev11-Oct-14 16:28 
QuestionI like the testing program, got that source? Pin
Marc Brooks17-Oct-05 5:14
Marc Brooks17-Oct-05 5:14 
AnswerRe: I like the testing program, got that source? Pin
Nikola Stepan18-Oct-05 1:35
Nikola Stepan18-Oct-05 1:35 
GeneralRe: I like the testing program, got that source? Pin
Marc Brooks18-Oct-05 5:03
Marc Brooks18-Oct-05 5:03 
GeneralRe: I like the testing program, got that source? Pin
Nikola Stepan19-Oct-05 20:27
Nikola Stepan19-Oct-05 20:27 
GeneralRe: I like the testing program, got that source? Pin
Anonymous25-Oct-05 5:15
Anonymous25-Oct-05 5:15 
GeneralRe: I like the testing program, got that source? Pin
Mackster21130-Nov-06 17:20
Mackster21130-Nov-06 17:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.