Help needed with Rational class for exercise in PPP2

Jan 17, 2017 at 3:11pm
It's as the title says. Especially, right now, I need help with defining addition, subtraction, division and multiplication operators for it, as well as assignment operators.

The exercise specifications are like this:
Design and implement a rational number class, Rational. A rational
number has two parts: a numerator and a denominator, for example, 5/6 (five-sixths, also known as approximately .83333). Look up the definition
if you need to. Provide assignment, addition, subtraction, multiplication,
division, and equality operators. Also, provide a conversion to double.
Why would people want to use a Rational class?


This is my code right now:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// chapter9ex13.cpp : Defines the entry point for the console application.
// Osman Zakir
// 1 / 17 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 9 Exercise 13
// Exercise Specifications:
/**
 * Design and implement a rational number class, Rational . A rational
 * number has two parts: a numerator and a denominator, for example, 5/6
 * (five-sixths, also known as approximately .83333). Look up the definition
 * if you need to. Provide assignment, addition, subtraction, multiplication,
 * division, and equality operators. Also, provide a conversion to double .
 * Why would people want to use a Rational class?
 */

#include "../../std_lib_facilities.h"

class Rational
{
private:
	int m_numerator;
	int m_denominator;
	void check_rational();
public:
	Rational(const int numerator = 0, const int denominator = 1);
	Rational();
	int numerator() const { return m_numerator; }
	int denominator() const { return m_denominator; }
	void set_numerator(const int value) { m_numerator = value; }
	void set_denominator(const int value) { m_denominator = value; }
	Rational operator=(Rational &rat);
	friend Rational operator+(Rational &rat1, Rational &rat2);
	friend Rational operator-(Rational &rat1, Rational &rat2);
	friend Rational operator*(Rational &rat1, Rational &rat2);
	friend Rational operator/(Rational &rat1, Rational &rat2);
	friend bool operator==(const Rational &rat1, const Rational &rat2);
	friend bool operator!=(const Rational &rat1, const Rational &rat2);
};

Rational operator+=(Rational &rat1, Rational &rat2);
Rational operator-=(Rational &rat1, Rational &rat2);
Rational operator*=(Rational &rat1, Rational &rat2);
Rational operator/=(Rational &rat1, Rational &rat2);

int main()
{
    return 0;
}

Rational::Rational(const int numerator, const int denominator)
	:m_numerator{ numerator }, m_denominator{ denominator }
{
	check_rational();
}

Rational::Rational()
	: m_numerator{ 0 }, m_denominator{ 1 }
{
}

void Rational::check_rational()
{
	if (m_denominator == 0)
	{
		error("rational number with denominator of 0");
	}
}


For the equality operators, I was thinking of doing this:
1
2
3
4
5
6
7
8
9
10
bool operator==(const Rational &rat1, const Rational &rat2)
{
	return rat1.numerator == rat2.numerator &&
		rat1.denominator == rat2.denominator;
}

bool operator!=(const Rational &rat1, const Rational &rat2)
{
	return !(rat1 == rat2);
}


And how do I provide a conversion to double?

Something like this?
1
2
3
4
5
6
7
Rational Rational::operator()(Rational &rational, double value)
{
	m_numerator = value;
	m_denominator = value;
	value = m_numerator;
	value = m_denominator;
	return *this;}


Would that be good enough? Thanks in advance for any help.
Last edited on Jan 17, 2017 at 3:22pm
Jan 17, 2017 at 3:35pm
Why would people want to use a Rational class?

- As a learning exercise?
- To represent fractions.

For the equality operators, I was thinking of doing this:

Consider 1/3 and 2/6? Should they compare as equal?


Line 25-26: Multiple default constructors.

And how do I provide a conversion to double?
1
2
3
Rational::operator double () const
{   return m_numerator / m_denominator;
}

Jan 17, 2017 at 4:01pm
The question about why people would use a Rational class is being asked as part of the exercise itself. I'm not the one asking that. Thanks, though.

Consider 1/3 and 2/6? Should they compare as equal?


Do I need to do that for operator overloading? I can just write a separate function for that that would check if two Rational class objects are equivalent, right?

As for Lines 25 and 26, I thought only the latter was a default constructor since it has an empty parameter list and sets the values to their defaults in the definition. I set default values for the one on Line 25 so that the fraction would have a default value for the denominator even if the user only provides one for the numerator. It's not a default constructor because the parameter list isn't empty. Are you saying it's a bad idea to have default values in a non-default constructor?

Thanks for the help on the conversion to double.

What about the other operators, though, like assignment and the ones for the mathematical operators?

How would this be for the assignment operator(=)?

1
2
3
4
5
6
Rational Rational::operator=(Rational &rat)
{
	m_numerator = rat.m_numerator;
	m_denominator = rat.m_denominator;
	return *this;
}


Edit: I defined the mathematical operators like so, for now:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Rational operator+(Rational &rat1, Rational &rat2)
{
	return rat1.m_numerator + rat2.m_numerator &&
		rat1.m_denominator + rat2.m_denominator;
}

Rational operator-(Rational &rat1, Rational &rat2)
{
	return rat1.m_numerator - rat2.m_numerator &&
		rat1.m_numerator - rat2.m_denominator;
}

Rational operator*(Rational &rat1, Rational &rat2)
{
	return rat1.m_numerator * rat2.m_numerator &&
		rat1.m_numerator * rat2.m_denominator;
}

Rational operator/(Rational &rat1, Rational &rat2)
{
	return rat1.m_numerator / rat2.m_numerator &&
		rat1.m_denominator / rat2.m_denominator;
}


Is that good? If so, I just need help on the compound assignment equivalents now.
Last edited on Jan 17, 2017 at 4:20pm
Jan 17, 2017 at 4:52pm
1
2
3
4
5
Rational operator+(Rational &rat1, Rational &rat2)
{
	return rat1.m_numerator + rat2.m_numerator &&
		rat1.m_denominator + rat2.m_denominator;
}

This is not correct. Imagine rat1 = 1/2 and rat2 = 2/3. Your result would be 3/5, but should be 3/6 + 4/6 which is 7/6
https://en.wikipedia.org/wiki/Fraction_(mathematics)#Addition
An online calculator to check your results:
http://www.calculatorsoup.com/calculators/math/fractions.php
Jan 17, 2017 at 5:23pm
Also, you're doing a logical AND operator (&&) on each of your return statements. That's going to give you a boolean result.

It's easiest if you create a temporary instance to hold the result:
1
2
3
4
5
6
7
8
//  Member operator 
Rational Rational::operator * (const Rational &rat2) const  //  Fix const correctness
{   Rational    temp;   //  Temporary instance for result

    temp.m_numerator = m_numerator * rat2.m_numerator; 
	temp.m_denominator = m_denominator * rat2.m_denominator;
	return temp;
}




Jan 17, 2017 at 6:17pm
So for multiplication and division, it's both the numerator and the denominator, but for addition and subtraction it's just the numerator? Would that be correct?

And should I make them member functions or friend functions? And what about the compound assignment operators?

Edit: I'm posting the whole thing as I have it now. Please help me fix any problems.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include "../../std_lib_facilities.h"

class Rational
{
private:
	int m_numerator;
	int m_denominator;
	void check_rational();
public:
	Rational(const int numerator, const int denominator = 1);
	Rational();
	int numerator() const { return m_numerator; }
	int denominator() const { return m_denominator; }
	void set_numerator(const int value) { m_numerator = value; }
	void set_denominator(const int value) { m_denominator = value; }
	Rational operator=(Rational &rat);
	friend Rational operator+(const Rational &rat, const int val);
	friend Rational operator-(const Rational &rat, const int val);
	friend Rational operator*(const Rational &rat, const int val);
	friend Rational operator/(const Rational &rat, const int val);
	friend Rational operator+(const Rational &rat1, const Rational &rat2);
	friend Rational operator-(const Rational &rat1, const Rational &rat2);
	friend Rational operator*(const Rational &rat1, const Rational &rat2);
	friend Rational operator/(const Rational &rat1, const Rational &rat2);
	friend bool operator==(const Rational &rat1, const Rational &rat2);
	friend bool operator!=(const Rational &rat1, const Rational &rat2);
	friend Rational operator+=(const Rational &rat, const int val);
	friend Rational operator-=(const Rational &rat, const int val);
	friend Rational operator*=(const Rational &rat, const int val);
	friend Rational operator/=(const Rational &rat, const int val);
	operator double() const;
};

ostream& operator<<(ostream &os, const Rational &rat);

int main()
{
	Rational rat1{ 5, 6 };
	double fract1 = double(rat1.numerator() / double(rat1.denominator()));
	cout << rat1 << '\n';
	cout << "That's " << fract1 << " as a decimal number.\n";
	keep_window_open();
}

Rational::Rational(const int numerator, const int denominator)
	:m_numerator{ numerator }, m_denominator{ denominator }
{
	check_rational();
}

Rational::Rational()
	: m_numerator{ 0 }, m_denominator{ 1 }
{
}

void Rational::check_rational()
{
	if (m_denominator == 0)
	{
		error("rational number with denominator of 0");
	}
}

bool operator==(const Rational &rat1, const Rational &rat2)
{
	return rat1.numerator() == rat2.numerator() &&
		rat1.denominator() == rat2.denominator();
}

bool operator!=(const Rational &rat1, const Rational &rat2)
{
	return !(rat1 == rat2);
}

Rational::operator double() const
{
	return double(double(m_numerator) / m_denominator);
}

Rational Rational::operator=(Rational &rat)
{
	m_numerator = rat.m_numerator;
	m_denominator = rat.m_denominator;
	return *this;
}

Rational operator+(const Rational &rat, const int val)
{
	return rat.m_numerator + val;
}

Rational operator+(const Rational &rat1, const Rational &rat2)
{
	return rat1.m_numerator + rat2.m_numerator;
}

Rational operator-(const Rational &rat1, const Rational &rat2)
{
	return rat1.m_numerator - rat2.m_numerator;
}

Rational operator*(const Rational &rat1, const Rational &rat2)
{
	Rational temp;
	temp.m_numerator = rat1.m_numerator * rat2.m_numerator;
	temp.m_denominator = rat1.m_denominator * rat2.m_denominator;
	return temp;
}

Rational operator/(const Rational &rat1, const Rational &rat2)
{
	Rational temp;
	temp.m_numerator = rat1.m_numerator / rat2.m_numerator;
	temp.m_denominator = rat1.m_denominator / rat2.m_denominator;
	return temp;
}

Rational operator-(const Rational &rat, const int val)
{
	return rat.m_numerator - val;
}

Rational operator*(const Rational &rat, const int val)
{
	Rational temp;
	temp.m_numerator = rat.m_numerator * val;
	temp.m_denominator = rat.m_denominator * val;
	return temp;
}

Rational operator/(const Rational &rat, const int val)
{
	Rational temp;
	temp.m_numerator = rat.m_numerator * val;
	temp.m_denominator = rat.m_denominator * val;
	return temp;
}

Rational operator+=(const Rational &rat, const int val)
{
	return Rational(rat += val);
}

Rational operator-=(const Rational &rat, const int val)
{
	return Rational(rat -= val);
}

Rational operator*=(const Rational &rat, const int val)
{
	return Rational(rat *= val);
}

Rational operator/=(const Rational &rat, const int val)
{
	return Rational(rat /= val);
}

ostream& operator<<(ostream &os, const Rational &rat)
{
	return os << rat.numerator() << '/' << rat.denominator();
}


I'm getting the following warnings on the code:

1>c:\users\osman\programming\visual studio 2015\projects\programming_principles_practice_using_c++\chapter9ex13\chapter9ex13\chapter9ex13.cpp(157): warning C4717: 'operator+=': recursive on all control paths, function will cause runtime stack overflow
1>c:\users\osman\programming\visual studio 2015\projects\programming_principles_practice_using_c++\chapter9ex13\chapter9ex13\chapter9ex13.cpp(167): warning C4717: 'operator*=': recursive on all control paths, function will cause runtime stack overflow
1>c:\users\osman\programming\visual studio 2015\projects\programming_principles_practice_using_c++\chapter9ex13\chapter9ex13\chapter9ex13.cpp(172): warning C4717: 'operator/=': recursive on all control paths, function will cause runtime stack overflow
1>c:\users\osman\programming\visual studio 2015\projects\programming_principles_practice_using_c++\chapter9ex13\chapter9ex13\chapter9ex13.cpp(162): warning C4717: 'operator-=': recursive on all control paths, function will cause runtime stack overflow
Last edited on Jan 17, 2017 at 7:30pm
Jan 17, 2017 at 7:36pm
As for Lines 25 and 26, I thought only the latter was a default constructor since it has an empty parameter list and sets the values to their defaults in the definition.
 
  Rational r;  

Which constructor is going to get called? Line 25 or line 26? The compiler can't decide because both arguments are defaulted on line 25.

It's not a bad idea to have constructors with default values. It's just that you can't have a constructor with all values defaulted along with a conventional default constructor. You have a couple of choices. 1) Change line 25 so that at least 1 argument is required. Or, 2) leave line 25 as is and eliminate line 26.
Jan 17, 2017 at 8:06pm
I went with the first option.

Anyway, I'll re-post this here (had it posted in my previous post originally);

I'm posting the whole thing as I have it now. Please help me fix any problems.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// chapter9ex13.cpp : Defines the entry point for the console application.
// Osman Zakir
// 1 / 17 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 9 Exercise 13
// Exercise Specifications:
/**
 * Design and implement a rational number class, Rational . A rational
 * number has two parts: a numerator and a denominator, for example, 5/6
 * (five-sixths, also known as approximately .83333). Look up the definition
 * if you need to. Provide assignment, addition, subtraction, multiplication,
 * division, and equality operators. Also, provide a conversion to double .
 * Why would people want to use a Rational class?
 */

#include "../../std_lib_facilities.h"

class Rational
{
private:
	int m_numerator;
	int m_denominator;
	void check_rational();
public:
	Rational(const int numerator, const int denominator = 1);
	Rational();
	int numerator() const { return m_numerator; }
	int denominator() const { return m_denominator; }
	void set_numerator(const int value) { m_numerator = value; }
	void set_denominator(const int value) { m_denominator = value; }
	Rational operator=(Rational &rat);
	friend Rational operator+(const Rational &rat, const int val);
	friend Rational operator-(const Rational &rat, const int val);
	friend Rational operator*(const Rational &rat, const int val);
	friend Rational operator/(const Rational &rat, const int val);
	friend Rational operator+(const Rational &rat1, const Rational &rat2);
	friend Rational operator-(const Rational &rat1, const Rational &rat2);
	friend Rational operator*(const Rational &rat1, const Rational &rat2);
	friend Rational operator/(const Rational &rat1, const Rational &rat2);
	friend bool operator==(const Rational &rat1, const Rational &rat2);
	friend bool operator!=(const Rational &rat1, const Rational &rat2);
	friend Rational operator+=(const Rational &rat, const int val);
	friend Rational operator-=(const Rational &rat, const int val);
	friend Rational operator*=(const Rational &rat, const int val);
	friend Rational operator/=(const Rational &rat, const int val);
	operator double() const;
};

ostream& operator<<(ostream &os, const Rational &rat);

int main()
{
	Rational rat1{ 5, 6 };
	double fract1 = double(rat1.numerator() / double(rat1.denominator()));
	cout << rat1 << '\n';
	cout << "That's " << fract1 << " as a decimal number.\n";
	keep_window_open();
}

Rational::Rational(const int numerator, const int denominator)
	:m_numerator{ numerator }, m_denominator{ denominator }
{
	check_rational();
}

Rational::Rational()
	: m_numerator{ 0 }, m_denominator{ 1 }
{
}

void Rational::check_rational()
{
	if (m_denominator == 0)
	{
		error("rational number with denominator of 0");
	}
}

bool operator==(const Rational &rat1, const Rational &rat2)
{
	return rat1.numerator() == rat2.numerator() &&
		rat1.denominator() == rat2.denominator();
}

bool operator!=(const Rational &rat1, const Rational &rat2)
{
	return !(rat1 == rat2);
}

Rational::operator double() const
{
	return double(double(m_numerator) / m_denominator);
}

Rational Rational::operator=(Rational &rat)
{
	m_numerator = rat.m_numerator;
	m_denominator = rat.m_denominator;
	return *this;
}

Rational operator+(const Rational &rat, const int val)
{
	return rat.m_numerator + val;
}

Rational operator+(const Rational &rat1, const Rational &rat2)
{
	return rat1.m_numerator + rat2.m_numerator;
}

Rational operator-(const Rational &rat1, const Rational &rat2)
{
	return rat1.m_numerator - rat2.m_numerator;
}

Rational operator*(const Rational &rat1, const Rational &rat2)
{
	Rational temp;
	temp.m_numerator = rat1.m_numerator * rat2.m_numerator;
	temp.m_denominator = rat1.m_denominator * rat2.m_denominator;
	return temp;
}

Rational operator/(const Rational &rat1, const Rational &rat2)
{
	Rational temp;
	temp.m_numerator = rat1.m_numerator / rat2.m_numerator;
	temp.m_denominator = rat1.m_denominator / rat2.m_denominator;
	return temp;
}

Rational operator-(const Rational &rat, const int val)
{
	return rat.m_numerator - val;
}

Rational operator*(const Rational &rat, const int val)
{
	Rational temp;
	temp.m_numerator = rat.m_numerator * val;
	temp.m_denominator = rat.m_denominator * val;
	return temp;
}

Rational operator/(const Rational &rat, const int val)
{
	Rational temp;
	temp.m_numerator = rat.m_numerator * val;
	temp.m_denominator = rat.m_denominator * val;
	return temp;
}

Rational operator+=(const Rational &rat, const int val)
{
	return Rational(rat += val);
}

Rational operator-=(const Rational &rat, const int val)
{
	return Rational(rat -= val);
}

Rational operator*=(const Rational &rat, const int val)
{
	return Rational(rat *= val);
}

Rational operator/=(const Rational &rat, const int val)
{
	return Rational(rat /= val);
}

ostream& operator<<(ostream &os, const Rational &rat)
{
	return os << rat.numerator() << '/' << rat.denominator();
}


I'm getting the following warnings on the code:

1>c:\users\osman\programming\visual studio 2015\projects\programming_principles_practice_using_c++\chapter9ex13\chapter9ex13\chapter9ex13.cpp(157): warning C4717: 'operator+=': recursive on all control paths, function will cause runtime stack overflow
1>c:\users\osman\programming\visual studio 2015\projects\programming_principles_practice_using_c++\chapter9ex13\chapter9ex13\chapter9ex13.cpp(167): warning C4717: 'operator*=': recursive on all control paths, function will cause runtime stack overflow
1>c:\users\osman\programming\visual studio 2015\projects\programming_principles_practice_using_c++\chapter9ex13\chapter9ex13\chapter9ex13.cpp(172): warning C4717: 'operator/=': recursive on all control paths, function will cause runtime stack overflow
1>c:\users\osman\programming\visual studio 2015\projects\programming_principles_practice_using_c++\chapter9ex13\chapter9ex13\chapter9ex13.cpp(162): warning C4717: 'operator-=': recursive on all control paths, function will cause runtime stack overflow


Hope you can help me.
Last edited on Jan 17, 2017 at 8:07pm
Jan 17, 2017 at 8:43pm
See adding fractions:
https://www.mathsisfun.com/fractions_addition.html

In particular, look at the example for 1/3 + 1/5
result 8/15


Jan 17, 2017 at 8:53pm
I'm getting the following warnings on the code:


Line 156, 161, 166, 171: Your operator functions are calling themselves, which in term call themselves, ad naseum.
Jan 17, 2017 at 9:19pm
I get that. What I'm asking is how to fix it.

Edit: Could someone point me to a good application of Euclid's algorithm that could help me out here? Like if I want to simplify and compare fractions so I can add or subtract them.

I have it like this right now (I took out the compound assignment arithmetic operators for now):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// chapter9ex13.cpp : Defines the entry point for the console application.
// Osman Zakir
// 1 / 17 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 9 Exercise 13
// Exercise Specifications:
/**
 * Design and implement a rational number class, Rational . A rational
 * number has two parts: a numerator and a denominator, for example, 5/6
 * (five-sixths, also known as approximately .83333). Look up the definition
 * if you need to. Provide assignment, addition, subtraction, multiplication,
 * division, and equality operators. Also, provide a conversion to double .
 * Why would people want to use a Rational class?
 */

#include "../../std_lib_facilities.h"

class Rational
{
private:
	int m_numerator;
	int m_denominator;
	void check_rational();
public:
	Rational(const int numerator, const int denominator = 1);
	Rational();
	int numerator() const { return m_numerator; }
	int denominator() const { return m_denominator; }
	void set_numerator(const int value) { m_numerator = value; }
	void set_denominator(const int value) { m_denominator = value; }
	Rational operator=(Rational &rat);
	friend Rational operator+(const Rational &rat, const int val);
	friend Rational operator-(const Rational &rat, const int val);
	friend Rational operator*(const Rational &rat, const int val);
	friend Rational operator/(const Rational &rat, const int val);
	friend Rational operator+(const Rational &rat1, const Rational &rat2);
	friend Rational operator-(const Rational &rat1, const Rational &rat2);
	friend Rational operator*(const Rational &rat1, const Rational &rat2);
	friend Rational operator/(const Rational &rat1, const Rational &rat2);
	friend bool operator==(const Rational &rat1, const Rational &rat2);
	friend bool operator!=(const Rational &rat1, const Rational &rat2);
	operator double() const;
};

ostream& operator<<(ostream &os, const Rational &rat);
Rational get_input();
void print_rational(const Rational &rat);

int main()
{
	Rational rat = get_input();
	print_rational(rat);
	Rational rat2{ 4, 5 };
	keep_window_open();
}

Rational::Rational(const int numerator, const int denominator)
	:m_numerator{ numerator }, m_denominator{ denominator }
{
	check_rational();
}

Rational::Rational()
	: m_numerator{ 0 }, m_denominator{ 1 }
{
}

void Rational::check_rational()
{
	if (m_denominator == 0)
	{
		error("rational number with denominator of 0");
	}
}

bool operator==(const Rational &rat1, const Rational &rat2)
{
	return rat1.numerator() == rat2.numerator() &&
		rat1.denominator() == rat2.denominator();
}

bool operator!=(const Rational &rat1, const Rational &rat2)
{
	return !(rat1 == rat2);
}

Rational::operator double() const
{
	return double(double(m_numerator) / m_denominator);
}

Rational Rational::operator=(Rational &rat)
{
	m_numerator = rat.m_numerator;
	m_denominator = rat.m_denominator;
	return *this;
}

Rational operator+(const Rational &rat, const int val)
{
	return rat.m_numerator + val;
}

Rational operator+(const Rational &rat1, const Rational &rat2)
{
	return rat1.m_numerator + rat2.m_numerator;
}

Rational operator-(const Rational &rat1, const Rational &rat2)
{
	return rat1.m_numerator - rat2.m_numerator;
}

Rational operator*(const Rational &rat1, const Rational &rat2)
{
	Rational temp;
	temp.m_numerator = rat1.m_numerator * rat2.m_numerator;
	temp.m_denominator = rat1.m_denominator * rat2.m_denominator;
	return temp;
}

Rational operator/(const Rational &rat1, const Rational &rat2)
{
	Rational temp;
	temp.m_numerator = rat1.m_numerator / rat2.m_numerator;
	temp.m_denominator = rat1.m_denominator / rat2.m_denominator;
	return temp;
}

Rational operator-(const Rational &rat, const int val)
{
	return rat.m_numerator - val;
}

Rational operator*(const Rational &rat, const int val)
{
	Rational temp;
	temp.m_numerator = rat.m_numerator * val;
	temp.m_denominator = rat.m_denominator * val;
	return temp;
}

Rational operator/(const Rational &rat, const int val)
{
	Rational temp;
	temp.m_numerator = rat.m_numerator * val;
	temp.m_denominator = rat.m_denominator * val;
	return temp;
}

ostream& operator<<(ostream &os, const Rational &rat)
{
	return os << rat.numerator() << '/' << rat.denominator();
}

Rational get_input()
{
	cout << "Enter numerator: ";
	int num;
	cin >> num;
	cin.ignore();
	cout << "Enter denominator: ";
	int den;
	cin >> den;
	cin.ignore();
	Rational rat{ num, den };
	return rat;
}

void print_rational(const Rational &rat)
{
	cout << "You entered " << rat
		<< "\nAs a fraction, it is read as "
		<< double(rat.numerator() / double(rat.denominator()))
		<< '\n';
}
Last edited on Jan 17, 2017 at 10:56pm
Jan 17, 2017 at 11:42pm
Your arithmetic operators are all incorrect and/or do not represent the rational as a proper fraction. You'll need to get the greatest common divisor(GCD) and the lowest common multiple(LCM) to fix these issues.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int gcd( int a, int b )
{
	while( b != 0 ) {
		int temp{ b };
		b = a % b;
		a = temp;
	}

	return a;
}
int lcm( int a, int b )
{
	return a * b / gcd( a, b );
}
void to_proper( Rational& r )
{
	int reduce_factor{ gcd( r.num( ), r.den( ) ) };
	r.num( r.num( ) / reduce_factor );
	r.den( r.den( ) / reduce_factor );

	// to fix fractions like -1/-2
	if( r.num( ) < 0 && r.den( ) < 0 ) {
		r.num( -r.num( ) );
		r.den( -r.den( ) );
	}
}


Here's how you would use them to add.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Rational operator+( const Rational& lhs, const Rational& rhs )
{
	// common denominator
	int common{ lcm( lhs.den( ), rhs.den( ) ) },
		// what to multiply the numerators by when combining fractions
		// eg. 3/4 + 2/3
		// = ({3} * 3 + {4} * 2)/12
		// the multipliers are 3 and 4
		lhs_multi{ common / lhs.den( ) },
		rhs_multi{ common / rhs.den( ) };

	Rational r{
		lhs_multi * lhs.num( ) + rhs_multi * rhs.num( ),
		common
	};

	to_proper( r );

	return r;
}
Jan 17, 2017 at 11:52pm
Thanks. I'll study that and use it. For now I need to go to bed, though.

Edit: Let me know what you think of this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
#include "../../std_lib_facilities.h"

class Rational
{
private:
	int m_numerator;
	int m_denominator;
	void check_rational();
public:
	Rational(const int numerator, const int denominator = 1);
	Rational();
	int numerator() const { return m_numerator; }
	int denominator() const { return m_denominator; }
	void set_numerator(const int value) { m_numerator = value; }
	void set_denominator(const int value) { m_denominator = value; }
	void to_proper();
	Rational operator=(Rational &rat);
	// arithmetic operations for Rational and integer:
	friend Rational operator+(const Rational &rat, const int val);
	friend Rational operator-(const Rational &rat, const int val);
	friend Rational operator*(const Rational &rat, const int val);
	friend Rational operator/(const Rational &rat, const int val);
	// arithmetic operations for two Rationals:
	friend Rational operator+(const Rational &rat1, const Rational &rat2);
	friend Rational operator-(const Rational &rat1, const Rational &rat2);
	friend Rational operator*(const Rational &rat1, const Rational &rat2);
	friend Rational operator/(const Rational &rat1, const Rational &rat2);
	// equality operators:
	friend bool operator==(const Rational &rat1, const Rational &rat2);
	friend bool operator!=(const Rational &rat1, const Rational &rat2);
	operator double() const;	// for Rational to double conversion
};

ostream& operator<<(ostream &os, const Rational &rat);
Rational get_input();
char get_operation();
int gcd(int a, int b);
int lcm(int a, int b);
Rational calculate_rationals(Rational &rat1, Rational &rat2, char op);
Rational calculate_rat_int(Rational &rat, int val, char op);
void print_rational(const Rational &rat);

int main()
{
	try
	{
		cout << "Enter numerator and denominator for fraction: ";
		Rational rat = get_input();
		cout << "You entered ";
		print_rational(rat);
		char response = ' ';
		cout << "Do you want to do a calculation? (y/n): ";
		cin >> response;
		cin.ignore();
		if (response == 'Y' || response == 'y')
		{
			cout << "Rational and integer or rational and rational? ('rr' "
				<< " for rational and rational, 'ri' for rational and integer):\n";
			string answer;
			cin >> answer;
			cin.ignore();
			if (answer == "ri" || answer == "Ri" || answer == "RI")
			{
				cout << "Enter integer and operation: ";
				int number = 0;
				char operation = ' ';
				cin >> number >> operation;
				cin.ignore();
				Rational rat2 = calculate_rat_int(rat, number, operation);
				print_rational(rat2);
			}
			else if (answer == "rr" || answer == "Rr" || answer == "RR")
			{
				cout << "Enter numerator and denominator for fraction: ";
				Rational rat2 = get_input();
				cout << "Enter operation: ";
				char operation = get_operation();
				Rational rat3 = calculate_rationals(rat, rat2, operation);
				print_rational(rat3);
			}
			else
			{
				error("that's invalid input! Please try again later.");
			}
		}
		else if (response == 'N' || response == 'n')
		{
			keep_window_open();
			return 0;
		}
		else
		{
			error("that's not y or n! Please try again later.");
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}
	keep_window_open();
}

Rational::Rational(const int numerator, const int denominator)
	:m_numerator{ numerator }, m_denominator{ denominator }
{
	check_rational();
}

Rational::Rational()
	: m_numerator{ 0 }, m_denominator{ 1 }
{
}

void Rational::check_rational()
{
	if (m_denominator == 0)
	{
		error("rational number with denominator of 0");
	}
}

bool operator==(const Rational &rat1, const Rational &rat2)
{
	return rat1.denominator() == rat2.denominator();
}

bool operator!=(const Rational &rat1, const Rational &rat2)
{
	return !(rat1 == rat2);
}

Rational::operator double() const
{
	return double(double(m_numerator) / m_denominator);
}

Rational Rational::operator=(Rational &rat)
{
	m_numerator = rat.m_numerator;
	m_denominator = rat.m_denominator;
	return *this;
}

Rational operator+(const Rational &rat1, const Rational &rat2)
{
	int common = lcm(rat1.m_denominator, rat2.m_denominator);
	int rat1_multi = common / rat1.m_denominator;
	int rat2_multi = common / rat2.m_denominator;

	Rational rat{ rat1_multi * rat1.m_numerator + rat2_multi * rat2.m_numerator, common };
	rat.to_proper();
	return rat;
}

Rational operator-(const Rational &rat1, const Rational &rat2)
{
	int common = lcm(rat1.m_denominator, rat2.m_denominator);
	int rat1_multi = common / rat1.m_denominator;
	int rat2_multi = common / rat2.m_denominator;

	Rational rat{ rat1_multi * rat1.m_numerator - rat2_multi * rat2.m_numerator, common };
	rat.to_proper();
	return rat;
}

Rational operator*(const Rational &rat1, const Rational &rat2)
{
	Rational temp;
	temp.m_numerator = rat1.m_numerator * rat2.m_numerator;
	temp.m_denominator = rat1.m_denominator * rat2.m_denominator;
	return temp;
}

Rational operator/(const Rational &rat1, const Rational &rat2)
{
	Rational temp;
	temp.m_numerator = rat1.m_numerator / rat2.m_numerator;
	temp.m_denominator = rat1.m_denominator / rat2.m_denominator;
	return temp;
}

Rational operator+(const Rational &rat1, const int val)
{
	Rational rat2 = Rational(val);
	rat2.set_denominator(1);
	int common = lcm(rat1.m_denominator, rat2.m_denominator);
	int rat1_multi = common / rat1.m_denominator;
	int rat2_multi = common / rat2.m_denominator;

	Rational rat{ rat1_multi * rat1.m_numerator + rat2_multi * rat2.m_numerator, common };
	rat.to_proper();
	return rat;
}

Rational operator-(const Rational &rat1, const int val)
{
	Rational rat2 = Rational(val);
	rat2.set_denominator(1);
	int common = lcm(rat1.m_denominator, rat2.m_denominator);
	int rat1_multi = common / rat1.m_denominator;
	int rat2_multi = common / rat2.m_denominator;

	Rational rat{ rat1_multi * rat1.m_numerator - rat2_multi * rat2.m_numerator, common };
	rat.to_proper();
	return rat;
}

Rational operator*(const Rational &rat, const int val)
{
	Rational temp;
	temp.m_numerator = rat.m_numerator * val;
	temp.m_denominator = rat.m_denominator * val;
	return temp;
}

Rational operator/(const Rational &rat, const int val)
{
	Rational temp;
	temp.m_numerator = rat.m_numerator * val;
	temp.m_denominator = rat.m_denominator * val;
	return temp;
}

ostream& operator<<(ostream &os, const Rational &rat)
{
	return os << rat.numerator() << '/' << rat.denominator();
}

Rational get_input()
{
	int num;
	cin >> num;
	cin.ignore();
	int den;
	cin >> den;
	cin.ignore();
	Rational rat{ num, den };
	return rat;
}

char get_operation()
{
	char op;
	cin >> op;
	cin.ignore();
	return op;
}

Rational calculate_rationals(Rational &rat1, Rational &rat2, char op)
{
	switch (op)
	{
	case '+':
		rat1 = rat1 + rat2;
		break;
	case '-':
		rat1 = rat1 - rat2;
		break;
	case '*':
		rat1 = rat1 * rat2;
		break;
	case '/':
		rat1 = rat1 / rat2;
		break;
	}
	return rat1;
}

Rational calculate_rat_int(Rational &rat, int val, char op)
{
	switch (op)
	{
	case '+':
		rat = rat + val;
		break;
	case '-':
		rat = rat - val;
		break;
	case '*':
		rat = rat * val;
		break;
	case '/':
		rat = rat / val;
		break;
	}
	return rat;
}

int gcd(int a, int b)
{
	while (b != 0)
	{
		int temp = b;
		b = a % b;
		a = temp;
	}
	return a;
}

int lcm(int a, int b)
{
	return a * b / gcd(a, b);
}

void Rational::to_proper()
{
	int reduce_factor = gcd(m_numerator, m_denominator);
	m_numerator = m_numerator / reduce_factor;
	m_denominator = m_denominator / reduce_factor;

	if (numerator() < 0 && denominator() < 0)
	{
		m_numerator = -m_numerator;
		m_denominator = -m_denominator;
	}
}

void print_rational(const Rational &rat)
{
	cout << rat << "\nAs a decimal number, it is read as "
		<< double(rat.numerator() / double(rat.denominator()))
		<< '\n';
}
Last edited on Jan 18, 2017 at 12:28pm
Topic archived. No new replies allowed.