The Rational Class in C++

This section defines the Rational class for modeling rational numbers.

A rational number has a numerator and a denominator in the form a/b where a is the numera­tor and b is the denominator. For example, 1/3, 3/4, and 10/4 are rational numbers.

A rational number cannot have a denominator of 0, but a numerator of 0 is fine. Every inte­ger i is equivalent to a rational number i/1. Rational numbers are used in exact computations involving fractions—for example, 1/3 = 0.33333…. This number cannot be precisely represented in floating-point format using data type double or float. To obtain the exact result, we must use rational numbers.

C++ provides data types for integers and floating-point numbers but not for rational num­bers. This section shows how to design a class to represent rational numbers.

A Rational number can be represented using two data fields: numerator and denomi­nator. You can create a Rational number with specified numerator and denominator or create a default Rational number with numerator 0 and denominator 1. You can add, sub­tract, multiply, divide, and compare rational numbers. You can also convert a rational number into an integer, floating-point value, or string. The UML class diagram for the Rational class is given in Figure 14.1.

A rational number consists of a numerator and a denominator. There are many equivalent rational numbers; for example, 1/3 = 2/6 = 3/9 = 4/12. For convenience, we use 1/3 to represent all rational numbers that are equivalent to 1/3. The numerator and the denominator of 1/3 have no common divisor except 1, so 1/3 is said to be in lowest terms.

To reduce a rational number to its lowest terms, you need to find the greatest common divisor (GCD) of the absolute values of its numerator and denominator and then divide both numerator and denominator by this value. You can use the function for computing the GCD of two integers n and d, as suggested in Listing 6.4, GreatestCommonDivisor.cpp. The numera­tor and denominator in a Rational object are reduced to their lowest terms.

As usual, we first write a test program to create Rational objects and test the functions in the Rational class. Listing 14.1 shows the header file for the Rational class, and Listing 14.2 is a test program.

Listing 14.1 Rational.h

1 #ifndef RATIONAL_H
2
#define RATIONAL_H
3
#include <string>
4
using namespace std;
5
6
class Rational
7 {
public:
9     Rational();
10    Rational(
int numerator, int denominator);
11   
int getNumerator() const;
12   
int getDenominator() const;
13    Rational add(
const Rational& secondRational) const;
14    Rational subtract(
const Rational& secondRational) const;
15    Rational multiply(
const Rational& secondRational) const;
16    Rational divide(
const Rational& secondRational) const;
17   
int compareTo(const Rational& secondRational) const;
18   
bool equals(const Rational& secondRational) const;
19   
int intValue() const;
20   
double doubleValue() const;
21    string toString()
const;
22
23
private:
24   
int numerator;
25   
int denominator;
26   
static int gcd(int n, int d);
27 };
28
29
#endif

Listing 14.2 TestRationalClass.cpp

1 #include <iostream>
2
#include “Rational.h”
3 using namespace std;
4
5
int main()
6 {
7     
// Create and initialize two rational numbers r1 and r2
8     Rational r1(4, 2);
9     Rational r2(
2, 3);
10
11   
// Test toString, add, subtract, multiply, and divide
12    cout << r1.toString() << ” + ” << r2.toString() << ” = ”
13      << r1.add(r2).toString() << endl;
14    cout << r1.toString() <<
” – ” << r2.toString() << ” = ”
15      << r1.subtract(r2).toString() << endl;
16    cout << r1.toString() <<
” * ” << r2.toString() << ” = “

17      << r1.multiply(r2).toString() << endl;
18    cout << r1.toString() <<
” / ” << r2.toString() << ” = ”
19      << r1.divide(r2).toString() << endl;
20
21   
// Test intValue and double
22    cout << “r2.intValue()” << ” is ” << r2.intValue() << endl;
23    cout <<
“r2.doubleValue()” << ” is ” << r2.doubleValue() << endl;
24
25   
// Test compareTo and equal
26    cout << “r1.compareTo(r2) is ” << r1.compareTo(r2) << endl;
27    cout <<
“r2.compareTo(r1) is ” << r2.compareTo(r1) << endl;
28    cout <<
“r1.compareTo(r1) is ” << r1.compareTo(r1) << endl;
29    cout <<
“r1.equals(r1) is ”
30      << (r1.equals(r1) ? “true” : “false”) << endl;
31    cout <<
“r1.equals(r2) is ”
32      << (r1.equals(r2) ? “true” : “false”) << endl;
33
34   
return 0;
35 }

The main function creates two rational numbers, r1 and r2 (lines 8-9), and displays the results of r1 + r2, r1 – r2, r1 x r2, and r1 / r2 (lines 12-19). To perform r1 + r2, invoke r1.add(r2) to return a new Rational object. Similarly, r1.subtract(r2) returns a new Rational object for r1 – r2, r1.multiply(r2) for r1 x r2 , and r1.divide(r2)forr1 / r2.

The intValue() function displays the int value of r2 (line 22). The doubleValue() function displays the double value of r2 (line 23).

Invoking r1.compareTo(r2) (line 26) returns 1, since r1 is greater than r2. Invok­ing r2.compareTo(r1) (line 27) returns -1, since r2 is less than r1. Invoking r1.compareTo(r1) (line 28) returns 0, since r1 is equal to r1. Invoking r1.equals(r1) (line 29) returns true, since r1 is equal to r1. Invoking r1.equals(r2) (line 30) returns false, since r1 are r2 are not equal.

The Rational class is implemented in Listing 14.3.

Listing 14.3 Rational.cpp

1 #include “Rational.h”
2 #include <sstream> // Used in toString to convert numbers to strings
3 #include <cstdlib> // For the abs function
4 Rational::Rational()
5 {
6     numerator =
0;
7     denominator =
1;

8 }
9
10 Rational::Rational(
int numerator, int denominator)
11 {
12   
int factor = gcd(numerator, denominator);
13   
this->numerator = ((denominator > 0) ? 1 : –1) * numerator / factor;
14   
this->denominator = abs(denominator) / factor;
15 }
16
17
int Rational::getNumerator() const
18 {
19   
return numerator;
20 }
21
22
int Rational::getDenominator() const
23 {
24   
return denominator;
25 }
26
27
// Find GCD of two numbers
28 int Rational::gcd(int n, int d)
29 {
30   
int n1 = abs(n);
31   
int n2 = abs(d);
32   
int gcd = 1;
33
34   
for (int k = 1; k <= n1 && k <= n2; k++)
35    {
36       
if (n1 % k == 0 && n2 % k == 0)
37      gcd = k;
38    }
39
40   
return gcd;
41 }
42
43 Rational Rational::add(
const Rational& secondRational) const
44 {
45     
int n = numerator * secondRational.getDenominator() +
46    denominator * secondRational.getNumerator();
47   
int d = denominator * secondRational.getDenominator();
48   
return Rational(n, d);
49 }
50
51 Rational Rational::subtract(
const Rational& secondRational) const
52 {
53   
int n = numerator * secondRational.getDenominator()
54    – denominator * secondRational.getNumerator();
55   
int d = denominator * secondRational.getDenominator();
56   
return Rational(n, d);
57 }
58
59 Rational Rational::multiply(
const Rational& secondRational) const
60 {
61   
int n = numerator * secondRational.getNumerator();
62   
int d = denominator * secondRational.getDenominator();
63   
return Rational(n, d);
64 }
65
66 Rational Rational::divide(
const Rational& secondRational) const

67 {
68   
int n = numerator * secondRational.getDenominator();
69   
int d = denominator * secondRational.numerator;
70   
return Rational(n, d);
71 }
72
73
int Rational::compareTo(const Rational& secondRational) const
74 {
75    Rational temp = subtract(secondRational);
76   
if (temp.getNumerator() < 0)
77       
return 1;
78   
else if (temp.getNumerator() == 0)
79       
return 0;
80   
else
81       return 1;
82 }
83
84
bool Rational::equals(const Rational& secondRational) const
85 {
86   
if (compareTo(secondRational) == 0)
87       
return true;
88   
else
89       return false;
90 }
91
92
int Rational::intValue() const
93 {
94   
return getNumerator() / getDenominator();
95 }
96
97
double Rational::doubleValue() const
98 {
99   
return 1.0 * getNumerator() / getDenominator();
100 }
101
102 string Rational::toString()
const
103 {
104     stringstream ss;
105     ss << numerator;
106
107     
if (denominator > 1)
108     ss <<
“/” << denominator;
109
110     
return ss.str();
111 }

The rational number is encapsulated in a Rational object. Internally, a rational number is represented in its lowest terms (lines 13-14), and the numerator determines its sign (line 13). The denominator is always positive (line 14).

The gcd() function (lines 28-41) is private; it is not intended for use by clients. The gcd() function is only for internal use by the Rational class. The gcd() function is also static, since it is not dependent on any particular Rational object.

The abs(x) function (lines 30-31) is defined in the standard C++ library that returns the absolute value of x.

Two Rational objects can perform add, subtract, multiply, and divide operations. These functions return a new Rational object (lines 43-71).

The compareTo(&secondRationa1) function (lines 73-82) compares this rational number to the other rational number. It first subtracts the second rational from this rational and saves the result in temp (line 75). Return -1, 0, or 1, if temp’s numerator is less than, equal to, or greater than 0.

The equals(&secondRational) function (lines 84-90) utilizes the compareTo func­tion to compare this rational number to the other one. If this function returns 0, the equals function returns true; otherwise, it returns false.

The functions intValue and doubleValue return an int and a double value, respec­tively, for this rational number (lines 92-100).

The toString() function (lines 102-111) returns a string representation of a Rational object in the form numerator/denominator or simply numerator if denominator is 1. The string stream is used here to convert a number into a string, which was introduced in Section 10.2.11, “Converting Numbers to Strings.”

Source: Liang Y. Daniel (2013), Introduction to programming with C++, Pearson; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *