BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Algorithms and Data Structures
.NET 1.1+

Rational Number Arithmetic

Standard arithmetic uses integers or floating-point values for operands and results. Rational numbers, or fractions, are not amongst the standard data types. In this article, we will create a structure for fractions and add arithmetic functionality.

Rational Numbers

A rational number is a value that can be expressed as the ratio of two integer values. Such a number can be written as a fraction containing the two integers, one above the other, separated by a horizontal line. For example, ¼, ½ or ¾. The two numbers are called the numerator and denominator; the numerator appearing at the top of the fraction and the denominator at the bottom.

The use of rational numbers for arithmetic has some benefits over the use of decimal values. The main advantage is one of accuracy. With decimal values, rounding errors can occur in small numbers due to the manner in which the values are stored. This can be seen in the following sample code. Although the initial value is divided and then multiplied by the same number, the result is not the same as the original value. If rational numbers were used, this rounding problem would not be introduced.

decimal d = 1;
d /= 3;
d *= 3;

Console.WriteLine(d);   // Outputs "0.9999999999999999999999999999"

Creating the RationalNumber Structure

In this article we will develop a new structure to hold and manipulate rational numbers. The RationalNumber structure could just as easily be created as a class. However, in this case a structure is preferred so that rational numbers exhibit the behaviour of value types, rather than reference types.

For simplicity in this article, the RationalNumber structure will be created in a console application project. You may wish to create the collection in a class library instead for use within your own projects. To begin, create a new console application named "RationalNumberDemo". Add a new class file to the project named "RationalNumber".

The new code class will contain the standard declaration for a class. Replace this with the following structure definition.

struct RationalNumber
{
}

Creating the Properties

The RationalNumber structure requires two properties. These are the integer values representing the numerator and the denominator of the fraction. Each will be a read-only property as the setting of these values will be restricted to the constructor.

To add the properties, add the following code to the structure:

private int _numerator;
private int _denominator;

public int Numerator
{
    get { return _numerator; }
}

public int Denominator
{
    get { return _denominator; }
}

Adding the Constructor

A single constructor will be responsible for setting the values of the numerator and denominator. Add the following constructor code:

public RationalNumber(int numerator, int denominator)
{
    if (denominator == 0)
        throw new ArgumentException("The denominator must be non-zero.");

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

    _numerator = numerator;
    _denominator = denominator;

    ReduceToLowestTerms();
}

The constructor performs four key processes. Firstly, the denominator is validated to ensure that it is not zero. If a zero were permitted, the value of the entire fraction would always be infinity and trying to evaluate this value would cause a division by zero exception.

Once validated, the denominator is checked to determine if it is negative. Although it would be quite acceptable to have a negative denominator, it is more usual to express a fraction with a positive one. To achieve this, if the denominator is negative, both it and that numerator are multiplied by -1. This does not change the overall value of the fraction, just its representation.

The third stage is to store the numerator and denominator in the backing store fields for their respective properties. Once stored, the ReduceToLowestTerms method is called. This method is described below.

Reducing the Fraction to its Lowest Terms

A single rational number may be expressed as many different fractions. For example, one half (½) may be written with a numerator of two and a denominator of four, or as 4/8 or 5/10. Each of these representations is valid but generally it is preferred to show a fraction in its lowest terms. This means that the numerator and denominator are the smallest numbers possible for the rational number.

To reduce a fraction to its lowest terms, the greatest common divisor (GCD) of the numerator and denominator must be found. This is the largest number that both parts of the fraction may be divided by, whilst retaining two integers. The ReduceToLowestTerms method calls a further method to determine the GCD and divides the numerator and denominator by this value.

Add the ReduceToLowestTerms method to the structure:

private void ReduceToLowestTerms()
{
    int greatestCommonDivisor = RationalNumber.GetGCD(_numerator, _denominator);
    _numerator /= greatestCommonDivisor;
    _denominator /= greatestCommonDivisor;
}
31 May 2008