CIS 250 - Rational Number Class

Objectives

  1. Create a class (Rational) conforming to an API (Rational.h, Number.h).
  2. Create a sublass (Rational is a subclass of Number).
  3. Use a templated class (Number).
  4. Initialize and use a static variable within a class (display_style).
  5. Create constructors, including a copy constructor.
  6. Create a common initialization function for the constructors to use.
  7. Create accessor methods.
  8. Overload unary and binary operators.
  9. Overload the insertion and extraction operators using friend functions.
  10. Use exceptions to indicate errors.

Rational numbers are numbers which can be represented as fractions. They have an integer numerator and denominator, where the denominator is not allowed to be 0. You are given a base class (superclass) which your Rational class will be based on. You are given the header file for the class you are writing. You are also given the code for a testing program which should produce the sample output included later in this page.

This is a large and complex assignment - but a lot of it is repetitive. We will be doing a few small pieces of the implementation during classroom demonstration of some of these concepts. I would suggest you make good friends with someone in the class if you miss that class session so you can get the notes/code. If you find that any one of the functions you are writing gets to ten lines in length, then please ask for assistance. Most of the functions should be one to three lines of code within their function body. A few functions (gcd(int,int), setNums(int,int), operator<<, operator>>) will probably be five to eight lines of code.

Resources

  1. Number.h (templated base class)
  2. Rational.h (header file)
  3. Rational.cpp (Rational class implementation)
  4. TestRational.cpp (test driver code)

Assignment notes

  1. The only file you should be modifying is Rational.cpp
  2. Add documentation comments to Rational.cpp
  3. The starting files will be made available on the server we are using in the ~dklick/examples/cis250/rational directory.
  4. Some of the functions in Rational.cpp may already be completed for you. You're welcome. There is also a Complex number class demonstration in the CIS 250 demonstration programs list which may be helpful.
  5. The display_style variable should be initialized to Rational::FRACTIONAL. This is a static variable, so it must be initialized separately. You will have to use the Rational:: scope resolution more than once in this statement.
  6. There must be a function setDisplayStyle(DisplayStyle) which sets the static display_style variable to the value passed to it.
  7. There should be a private init function which takes two int values and sets the numerator and denominator from those values. It must do error checking and simplification. That is what the setNums(int, int) function is for. All of the constructors should use the init(int, int) function.
  8. There should be a default constructor that sets both the numerator and denominator to 1. Use the init(int,int) function.
  9. There should be a constructor that takes two int values and sets the numerator and denominator to those values. Use the init(int,int) function.
  10. There should be a copy constructor that sets the numerator and denominator to the value of the object passed in. Use the init(int,int) function.
  11. Whenever the numerator and denominator are set, several error checks and a simplification must be done. That's what the setNums(int,int) function is for. The init(int,int) function should use setNums(int,int) since it is available.
  12. The setNums(int,int) function must do the following:
  13. The simplify() function just has to divide the numerator and denominator by their greatest common divisor.
  14. Here's a recursive greatest common divisor function you can use:
    int Rational::gcd(const int n1, const int n2) const {
       int x = n1, y = n2;
       if (x < 0) x = -x;
       if (y > 0) y = -y;
       if (y==0) return x;
       else return gcd(y, x%y);
    }
  15. Create two accessor methods: getNumerator() and getDenominator()
  16. Overload the addition operator. It should return a new Rational object with the appropriate numerator and denominator. Addition is defined as follows: a/b + c/d ==> (a*d + c*b)/(b*d)
  17. Overload the subtraction operator. It should return a new Rational object with the appropriate numerator and denominator. Subtraction is defined as follows: a/b - c/d ==> (a*d - c*b)/(b*d)
  18. Overload the multiplication operator. It should return a new Rational object with the appropriate numerator and denominator. Multiplication is defined as follows: a/b * c/d ==> (a*c)/(b*d)
  19. Overload the division operator. It should return a new Rational object with the appropriate numerator and denominator. Division is defined as follows: a/b / c/d ==> (a*d)/(c*b)
  20. It should be noted that we have ignored what happens with division when the second Rational number is 0. In a full implementation, we would expect to throw an exception.
  21. Overload the assignment (=) operator and the following compound assignment operators: +=, -=, *=, /=.
  22. Overload the relational operators: = =, !=, <, <=, >, >=
  23. Overload the pre and post versions of the increment (++) and decrement (--) operators.
  24. The extraction operator (input) should ask for a numerator and denominator and set the object passed in appropriately using setNums(int,int).
  25. The insertion operator (output) should display the Rational number in fractional form if the display_style variable is set to FRACTIONAL, but in floating point form if the display_style variable is set to FLOATING_POINT.
  26. It should be fairly obvious what the other functions specified in the header file are supposed to do.

Grading rubric

Note: Program must be able to be compiled and run to get any points.

  1. 5 pts: style conventions followed (no tabs, proper indentation, documentation comments)
  2. 45 pts: whole program functions properly and follows requirements

Sample output

r[0] = 1/1
r[1] = 2/3
r[2] = 4/5
r[3] = 5/4
r[4] = 4/3
r[5] = 3/2
r[6] = 0/1
Testing math operators:
r[1] + r[4] = 2/1
r[1] - r[4] = -2/3
r[1] / r[4] = 1/2
r[1] * r[4] = 8/9
r[1] * r[5] = 1/1
r[5] - r[5] = 0/1
Trying an invalid division:
Invalid rational number: 5/0
Testing increment and decrement operators:
++r[1] = 5/3
--r[1] = 2/3
r[1]++ = 2/3
r[1]-- = 5/3
r[1] = 2/3
Testing relational and assignment operators:
r[2] set to r[0]
r[0] > r[1] = true
r[0] < r[1] = false
r[0] == r[1] = false
r[0] != r[1] = true
r[0] == r[2] = true
r[0] <= r[1] = false
r[0] >= r[1] = true
r[0] >= r[2] = true
r[0] <= r[2] = true
Testing display style setting:
r[0] = 1/1 = 1
r[1] = 2/3 = 0.666667
r[2] = 1/1 = 1
r[3] = 5/4 = 1.25
r[4] = 4/3 = 1.33333
r[5] = 3/2 = 1.5
Try to create invalid Rational object:
Invalid rational number: 2/0
Try to set invalid denominator:
Invalid rational number: -7/0
Test of extraction operator overload
Enter numerator: 63
Enter denominator: 9
You entered: 7/1
*** End of test