CIS 250 - Rational Number Class
Objectives
- Create a class (Rational) conforming to an API (Rational.h, Number.h).
- Create a sublass (Rational is a subclass of Number).
- Use a templated class (Number).
- Initialize and use a static variable within a class (display_style).
- Create constructors, including a copy constructor.
- Create a common initialization function for the constructors to use.
- Create accessor methods.
- Overload unary and binary operators.
- Overload the insertion and extraction operators using friend functions.
- 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
- Number.h (templated base class)
- Rational.h (header file)
- Rational.cpp (Rational class implementation)
- TestRational.cpp (test driver code)
Assignment notes
- The only file you should be modifying is Rational.cpp
- Add documentation comments to Rational.cpp
- The starting files will be made available on the server we are using in the
~dklick/examples/cis250/rational directory.
- 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.
- 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.
- There must be a function setDisplayStyle(DisplayStyle) which sets the
static display_style variable to the value passed to it.
- 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.
- There should be a default constructor that sets both the numerator and
denominator to 1. Use the init(int,int) function.
- 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.
- 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.
- 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.
- The setNums(int,int) function must do the following:
- If the denominator is 0, then throw an invalid_argument exception with the
message, "Invalid rational number: n/d" (where n and d are replaced by the numerator
and denominator). The invalid_argument class is in the stdexcept
library. You can use a char array and sprintf to create the error message.
- Set the object's numerator and denominator from the suggested values.
- If the numerator is 0, then make the denominator 1.
- If the requested denominator is negative, reverse the sign of both
the requested numerator and requested denominator.
- Call the simplify() function.
- The simplify() function just has to divide the numerator and denominator
by their greatest common divisor.
- 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);
}
- Create two accessor methods: getNumerator() and getDenominator()
- 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)
- 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)
- 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)
- 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)
- 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.
- Overload the assignment (=) operator and the following compound assignment operators:
+=, -=, *=, /=.
- Overload the relational operators: = =, !=, <, <=, >, >=
- Overload the pre and post versions of the increment (++) and decrement (--) operators.
- The extraction operator (input) should ask for a numerator and denominator
and set the object passed in appropriately using setNums(int,int).
- 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.
- 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.
- 5 pts: style conventions followed (no tabs, proper indentation, documentation comments)
- 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