# Rational Number Class

Provides a means of performing operations on Rational numbers that can be treated as fractions to make non-whole numbers easier to calculate and read than typical floating point storage types.

This was a project for a class. This is by no means an attempt to "reinvent the wheel" (The reason I say this is because I'm fairly certain the Boost libraries have an implementation of this class)

``````/**
* Rational.h
*/

#pragma once
#include <iostream>

using std::ostream;
using std::istream;

typedef int Rnumber;

class Rational{
private:
Rnumber numerator;
Rnumber denominator;
void reduce(Rnumber&, Rnumber&);
Rnumber gcd(Rnumber, Rnumber);

public:
Rational(Rnumber = 0, Rnumber = 1);
Rational invert() const;
Rational operator++(int);
Rational& operator++();
Rational operator--(int);
Rational& operator--();
Rational& operator=(const Rational&);
Rational operator+(const Rational&);
Rational operator*(const Rational&);
Rational operator-(const Rational&);
Rational operator/(const Rational&);
const bool operator<(const Rational&);
const bool operator<=(const Rational&);
const bool operator>(const Rational&);
const bool operator>=(const Rational&);
const bool operator==(const Rational&);
friend ostream& operator << (ostream&, const Rational&);
friend istream& operator >> (istream&, Rational&);
};

/////////////////////////////

/**
* Rational.cpp
*/

#include "Rational.h"
#include <cmath>

/**
* Constructor for Rational objects
*/
Rational::Rational(Rnumber lhs, Rnumber rhs){

/*Make temporary values for the Rational numbers*/
Rnumber x = lhs;
Rnumber y = rhs;

reduce(x, y); // reduce the Temporary values

/*Store the reduced values obtained from the function reduce into the global variables*/
this->numerator = x;
this->denominator = y;
}

/**
* Creates an inverted copy of this Rational object
*/
Rational Rational::invert() const{
return Rational(this->denominator, this->numerator);
}

/**
* Reduces the numerator and denominator to the smallest possible factors of the orignal value.
*/
void Rational::reduce(Rnumber& lhs, Rnumber& rhs){

/*A number is either negative or not - storing the state of negativity in bools for numerator and denominator*/
bool leftIsNegative = abs(lhs) != lhs;
bool rightIsNegative = abs(rhs) != rhs;

/*For simplicity I'm only going to work with positive numbers*/
Rnumber tempLeft = abs(lhs);
Rnumber tempRight = abs(rhs);

/*The lazy way of determining factors of numbers that can reduce the Rational*/
/*
for(int i = tempLeft; i > 1; i--){
if(tempLeft%i != 0) continue; // dont evaluate unnecessary values to boost performance

for(int j = tempRight; j > 1; j--){
if( ((tempLeft%i) == 0 && (tempRight%i) == 0 && (tempRight%j) == 0 ) ){
tempLeft = static_cast<int>(static_cast<double>(tempLeft)/i);
tempRight = static_cast<int>(static_cast<double>(tempRight)/i);
i = tempLeft + 1;
j = tempRight + 1;
}
}
}*/

/*Using the Euclidian algorithm to find the greatest common divisor between the numbers*/
Rnumber divisor = this->gcd(tempLeft, tempRight);
tempLeft /= divisor;
tempRight /= divisor;

/*Providing appropriate negative-number notion for the reduced number result*/
if( (leftIsNegative && (!rightIsNegative))
|| ((!leftIsNegative) && rightIsNegative ))
tempLeft *= -1;

/*Assigning the reduced values to the ones originally stored*/
lhs = tempLeft;
rhs = tempRight;
}

/**
* This function returns the greatest common divisor between two numbers
* (Thanks Ron - this is a lot more efficient than my original algorithm!)
*/
Rnumber Rational::gcd(Rnumber lhs, Rnumber rhs){

/*
Uses the division method to determine the greatest common divisor.
Example:

lhs = 5, rhs = 7

-gcd(5, 7) -> gcd(7, 5%7) -> gcd(7, 5)
-gcd(7, 5) -> gcd(5, 7%5) -> gcd(5, 2)
-gcd(5, 2) -> gcd(2, 5%2) -> gcd(2, 1)
-gcd(2, 1) -> gcd(1, 2%1) -> gcd(1, 0)
-gcd returns 1 because rhs = 0
*/
return ( rhs != 0 ? gcd(rhs, lhs % rhs) : lhs );
}

/**
* Increments this Rational by one and returns the previous version of this object before
* the increment [Postfix version].
*/
Rational Rational::operator++(int){
Rational temp = *this;
(*this) = Rational( this->denominator + this->numerator, this->denominator );
return temp;
}

/**
* Immediately increments this object by one [Prefix version].
*/
Rational& Rational::operator++(){
(*this)++;
return *this;
}

/**
* Decrements this Rational by one and returns the previous version of this object before
* the decrement [Postfix version].
*/
Rational Rational::operator--(int){
Rational temp = *this;
(*this) = Rational( this->numerator - this->denominator, this->denominator );
return temp;
}

/**
* Immediately decrements this object by one [Prefix version].
*/
Rational& Rational::operator--(){
(*this)--;
return *this;
}

/**
* Simply assigns the numerator and denominator of the argument Rational
* to the numerator and denominator of this rational.
*/
Rational& Rational::operator=(const Rational& other){
this->numerator = other.numerator;
this->denominator = other.denominator;
return *this;
}

/**
* The sum of this Rational and the argument Rational
*/
Rational Rational::operator+(const Rational& other){

/*
* This section of code needs an explanation.
* Suppose there are two Rationals, 3/4 and 5/6
* In order to perform addition properly, it is best to
* convert each Rational in an expanded form, such that
* the denominators are the same.
*
* 3/4 + 5/6 would expand to be 18/24 + 20/24
* The sum of the two rationals would be the sum of the new numerators
* divided by...
* the average of the value of denominators (which would just be the
* new denominator)
*/
Rnumber leftN = this->numerator * other.denominator;
Rnumber leftD = this->denominator * other.denominator;
Rnumber rightN = this->denominator * other.numerator;
Rnumber rightD = this->denominator * other.denominator;

Rnumber resultNumerator = leftN + rightN;
Rnumber resultDenominator = static_cast<int>(abs(leftD)); // both leftD and RightD are the same anyways

return Rational(resultNumerator, resultDenominator);
}

/**
* The product of this Rational and the other Rational
*/
Rational Rational::operator*(const Rational& other){

return Rational( this->numerator * other.numerator,
this->denominator * other.denominator);
}

/**
* The difference of this Rational and the argument Rational
*/
Rational Rational::operator-(const Rational& other){

/*
* This section of code needs an explanation.
* Suppose there are two Rationals, 3/4 and 5/6
* In order to perform addition properly, it is best to
* convert each Rational in an expanded form, such that
* the denominators are the same.
*
* 3/4 - 5/6 would expand to be 18/24 - 20/24
* The difference of the two rationals would be the difference of the new numerators
* divided by...
* the average of the value of denominators (which would just be the
* new denominator)
*/
Rnumber leftN = this->numerator * other.denominator;
Rnumber leftD = this->denominator * other.denominator;
Rnumber rightN = this->denominator * other.numerator;
Rnumber rightD = this->denominator * other.denominator;

Rnumber resultNumerator = leftN - rightN;
Rnumber resultDenominator = static_cast<int>(abs(leftD)); // both leftD and RightD are the same anyways

return Rational(resultNumerator, resultDenominator);
}

/**
* The quotient of this Rational and the other Rational
*/
Rational Rational::operator/(const Rational& other){

return (*this) * other.invert();
}

/**
* Tests is this Rational is less than the argument Rational
*/
const bool Rational::operator<(const Rational& other){
return (this->numerator * other.denominator) < (other.numerator * this->denominator);
}

/**
* Tests is this Rational is less than or equal to the argument Rational
*/
const bool Rational::operator<=(const Rational& other){
return ( (*this) < other ) || ( (*this) == other );
}

/**
* Tests is this Rational is greater than the argument Rational
*/
const bool Rational::operator>(const Rational& other){
//return (this->numerator * other.denominator) > (other.numerator * this->denominator);
return !( (*this) <= other ); // Will only be true when this Rational is greater than the other
}

/**
* Tests is this Rational is greater than or equal to the argument Rational
*/
const bool Rational::operator>=(const Rational& other){
return !( (*this) < other ); // Will only be true when this Rational is greater than or equal to the other
}

/**
* Tests for equivilance between two Rational objects.
*/
const bool Rational::operator==(const Rational& other){
return (this->numerator * other.denominator) == (this->denominator * other.numerator);
}

/**
* Allows the binary data contained within this Rational object to be
* easily pushed into an ostream type (which can by a file output stream,
* console output stream, application output stream, query output stream,
* Network output stream, etc).
*/
ostream& operator << (ostream& out, const Rational& rat){
return out << rat.numerator << "/" << rat.denominator;
}

/**
* Allows for a buffer with information stored inside it to write the binary data it contains
* into a data type stored in the argument Rational object.
*/
istream& operator >> (istream& in, Rational& rat){
Rnumber a = 0, b = 0;
in >> a >> b;
Rational temp (a, b);
rat = temp;
return in;
}

//////////////////////////////////

/**
* Alex Edwards
* 10/23/2008
* Project 2 : Rational
*/
#include <iostream>
#include "Rational.h"

const char* getBoolName(bool);

using std::cin;
using std::cout;
using std::endl;

/**
* Entry point of the program.
*/
int main(){

Rational a;
Rational b;
cout << "Enter the numerator and denominator for Rational a: ";
cin >> a;
cout << "\nEnter the numerator and denominator for Rational b: ";
cin >> b;
Rational s = (a + b);
Rational d = (a - b);
Rational q = (a / b);
Rational p = (a * b);
cout << "\nResults... \n" << endl;
cout << "Sum of a and b       : \t" << s << endl;
cout << "Difference of a and b: \t" << d << endl;
cout << "Quotient of a and b  : \t" << q << endl;
cout << "Product of a and b   : \t" << p << endl;
cout << "a < b : \t\t" << getBoolName((a < b)) << endl;
cout << "a <= b: \t\t" << getBoolName((a <= b)) << endl;
cout << "a == b: \t\t" << getBoolName((a == b)) << endl;
cout << "a >= b: \t\t" << getBoolName((a >= b)) << endl;
cout << "a > b : \t\t" << getBoolName((a > b)) << endl;
cout << "a++   : \t\t" << (a++, a) << endl;
cout << "b++   : \t\t" << (b++, b) << endl;
cout << "a--   : \t\t" << (a--, a) << endl;
cout << "b--   : \t\t" << (b--, b) << endl;
cout << "++a   : \t\t" << (++a) << endl;
cout << "++b   : \t\t" << (++b) << endl;
cout << "--a   : \t\t" << (--a) << endl;
cout << "--b   : \t\t" << (--b) << endl;
cin.ignore(INT_MAX, '\n');
cin.get();
return 0;
}

/**
* Convenience method for returning the stringified name of a boolean result.
*/
const char* getBoolName(bool arg){
return (arg) ? "True" : "False";
}``````
William Hemsworth 1,339

:) I once made practically the exact same thing, though I did it just for practice ;) Though, this looks much better than the one I made.
And hey, it helped me with my maths :D

Alex Edwards 321

As expected from you XP

And thanks! =)

But yeah... I'm planning on extending this class, but I'm not sure of how to do it.

For example, I want to make this class compatible with doubles so I'm thinking of making a double/double constructor, but then I'd have to worry about precision factors to convert the double into a big number, then use the gcd on it to convert it into an integer/integer value.

Maybe sometime later though @_@

William Hemsworth 1,339

Weeee, I found it :) See how much better yours looks :]?
And, thats weird, I don't even remember adding a parser to it ;)

``````#include <iostream>
#include <cmath>

class fraction {
// Returns true if text contains ch
static inline bool TextContains(char *text, char ch) {
while (*text) {
if (*text++ == ch) {
return true;
}
}
return false;
}

static inline void SubStr(char *text, char *target, int beg, int end) {
int len = end - beg;
memcpy_s(target, (rsize_t)len, &text[beg], (size_t)len);
target[len] = '\0';
}

static void GetWord(char *text, char *target, char *gaps, int bzIndex) {
int i = 0, sc = 0, ec = 0, g = 0;
for (; text[i] && TextContains(gaps,text[i]); i++);
for (sc = i; text[i]; i++) {
if (TextContains(gaps,text[i])) {
while (text[i] && TextContains(gaps,text[i + 1])) i++;
if (++g == bzIndex) sc = i + 1;
} else if (g == bzIndex) {
while(text[i] && !TextContains(gaps, text[i])) i++;
ec = i;
break;
}
}
SubStr(text, target, sc, ec);
};

public:
int Numerator;
int Denominator;
int count; // Whole numbers

fraction() {
count = 0;
Numerator = 0;
Denominator = 1;
}

fraction(int v) {
count = v;
Numerator = 0;
Denominator = 1;
}

fraction(int v, int n, int d) {
Numerator = n;
Denominator = d;

if (Denominator == 0) {
Numerator = 0;
Denominator = 1;
}

count = (Numerator > Denominator ?
Numerator / Denominator : 0) + v;

if (count != 0) {
Numerator -= (Numerator / Denominator) * Denominator;
}

Shrink();
}

fraction(int n, int d) {
Numerator = n;
Denominator = d;

if (Denominator == 0) {
Numerator = 0;
Denominator = 1;
}

count =  Numerator > Denominator ?
Numerator / Denominator : 0;

if (count != 0) {
Numerator -= (Numerator / Denominator) * Denominator;
}

Shrink();
}

fraction(double fraction2) {
int count = (int) fraction2;
unsigned char decimalCount = 0;
int den = 1;

while (ceil(fraction2) != fraction2) {
decimalCount++;
fraction2 *= 10;
den *= 10;
}

fraction2 -= (count * den);
if (den == 0) den = 1;
*this = fraction(count, (int) fraction2, den);
Shrink();
}

fraction(char *str) {
Parse(str);
}

inline bool Shrink() {
bool sucessful = 0;
for (int i = std::min(Numerator, Denominator); i; i--) {
if ((Numerator % i == 0) && (Denominator % i == 0)) {
Numerator /= i;
Denominator /= i;
sucessful = 1;
}
}
return sucessful;
}

fraction operator =(fraction &fraction2) {
count = fraction2.count;
Denominator = fraction2.Denominator;
Numerator = fraction2.Numerator;
return *this;
}

fraction operator =(int fraction2) {
count = fraction2;
Denominator = 1;
Numerator = 0;
return *this;
}

fraction operator =(double fraction2) {
*this = fraction(fraction2);
}

fraction operator =(char *str) {
Parse(str);
}

fraction operator +(fraction &fraction2) {
int n1 = Numerator + (count * Denominator);
int d1 = Denominator;
int n2 = fraction2.Numerator + (fraction2.count * fraction2.Denominator);
int d2 = fraction2.Denominator;
int MatchedDenominator = d1 * d2;
fraction f(
((MatchedDenominator / d1) * n1) +
((MatchedDenominator / d2) * n2)
,MatchedDenominator);
f.Shrink();
return f;
}

fraction operator -(fraction &fraction2) {
int n1 = Numerator + (count * Denominator);
int d1 = Denominator;
int n2 = fraction2.Numerator;
int d2 = fraction2.Denominator + (fraction2.count * fraction2.Denominator);
int MatchedDenominator = d1 * d2;
fraction f(
((MatchedDenominator / d1) * n1) -
((MatchedDenominator / d2) * n2)
,MatchedDenominator);
f.Shrink();
return f;
}

fraction operator *(fraction &fraction2) {
int n1 = Numerator + (count * Denominator);
int d1 = Denominator;
int n2 = fraction2.Numerator + (fraction2.count * fraction2.Denominator);
int d2 = fraction2.Denominator;
fraction f(n1 * n2, d1 * d2);
f.Shrink();
return f;
}

fraction operator /(fraction &fraction2) {
int n1 = Numerator + (count * Denominator);
int d1 = Denominator;
int n2 = fraction2.Numerator + (fraction2.count * fraction2.Denominator);
int d2 = fraction2.Denominator;
fraction f(n1 * d2, d1 * n2);
f.Shrink();
return f;
}

/* Overload operators with double */
fraction operator +(double fraction2) {
return (*this + fraction(fraction2));
}

fraction operator -(double fraction2) {
return (*this - fraction(fraction2));
}

fraction operator *(double fraction2) {
return (*this * fraction(fraction2));
}

fraction operator /(double fraction2) {
return (*this / fraction(fraction2));
}

/*   +=  -=  /=  *=    */
fraction operator +=(double fraction2) {
*this = *this + fraction(fraction2);
return *this;
}

fraction operator -=(double fraction2) {
*this = *this - fraction(fraction2);
return *this;
}

fraction operator *=(double fraction2) {
*this = *this * fraction(fraction2);
return *this;
}

fraction operator /=(double fraction2) {
*this = *this / fraction(fraction2);
return *this;
}

inline double Value() {
return (((double)Numerator+((double)count*(double)Denominator)) / (double)Denominator);
}

void Parse(char *str) {
size_t c = 0; // '/' count
for (register int i = 0; str[i]; i++, c += (str[i] == '/'));

if (c == 1) {
char temp1[5], temp2[5];

GetWord(str, temp1, " /", 0);
GetWord(str, temp2, " /", 1);

fraction fract(
atoi(temp1),
atoi(temp2)
);

*this = fract;
} // 1
else if (c == 2) {
char temp1[5], temp2[5], temp3[5];

GetWord(str, temp1, " /", 0);
GetWord(str, temp2, " /", 1);
GetWord(str, temp3, " /", 2);

fraction fract(
atoi(temp1),
atoi(temp2),
atoi(temp3)
);

*this = fract;
} // 2
else *this = 0;
}

#ifdef _CONSOLE
void Display() {
if (Numerator != 0) {
if (count != 0) {
std::cout << count << '/' << Numerator << '/' << Denominator;
} else {
std::cout << Numerator << '/' << Denominator;
}
} else {
std::cout << count;
}
}

friend std::ostream& operator <<(std::ostream& out, fraction &f) {
f.Display();
return out;
}

friend std::istream& operator >>(std::istream& in, fraction &f) {
char str[25];
std::cin.getline(str,25);
size_t c = 0; // '/' count

for (int i = 0; str[i]; i++, c += (str[i] == '/'));

if (c == 1) {
char temp1[5], temp2[5];

GetWord(str, temp1, " /", 0);
GetWord(str, temp2, " /", 0);

fraction fract(
atoi(temp1),
atoi(temp2)
);

f = fract;
} // 1

else if (c == 2) {
char temp1[5], temp2[5], temp3[5];

GetWord(str, temp1, " /", 0);
GetWord(str, temp2, " /", 0);
GetWord(str, temp3, " /", 0);

fraction fract(
atoi(temp1),
atoi(temp2),
atoi(temp3)
);

f = fract;
} // 2
else f = 0;
return in;
}
#endif
};

int main() {

fraction f = "3/33/99";
// 3/33/99 --> 3/1/3 --> 3.3333

f *= 2.5; // 3.3333 * 2.5 = 8.33325
f /= 67; // 8.33325 / 67 = 0.12437

std::cout << f; // 25/201  (0.12437)
std::cin.ignore();
return 0;
}``````

There's also the FPU-machine instruction: `fistp` Probably not portable to every platform, but simple. Agner Fog has an example of it in his Asmlib, and a bunch of other cool asm-to-C++ functions.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.