Reputation: 33624
I have to create a class called Fractions with 2 private fields Numerator, Denominator. And a public constructor that sets Numerator and Denominator to 1 by default. I have included 4 members functions in my my Fractions class: Sum, Difference, Product, Division.
Then I am not sure what to do next. Why does book show fraction equivalences? What do I have to do with that? I guess a very important question would be what parameters should my member functions take?
Also what would be a good way to prohibit denominator of 0? Throw exception or force it to be equal to 1?
Here is the complete source code for the question #5 and #6 (not shown) after struggling with the problem for days. Questions #6 just asks to implement the greatest common divisor function to return fraction in a simplified form. So here it is...
If you think there is a way to optimize this code I'd be happy to hear your responses!
#include <iostream>
using namespace std;
class Fraction
{
private:
int numerator, denominator;
public:
Fraction()
{
numerator = 1;
denominator = 1;
}
Fraction(int n, int d)
{
numerator = n;
if (d==0)
{
cout << "ERROR: ATTEMPTING TO DIVIDE BY ZERO" << endl;
exit(0); // will terminate the program if division by 0 is attempted
}
else
denominator = d;
}
/*In the following functions I am dividing both numerator and denominator by the gcd function.
GCD function accepts both numerator and denominator values. If we had 2 fractions, 1/2 and 1/4
and we passed it into the Sum, the result would be n=6 and d=8. These are the values that GCD
function will accept, find greatest common divisor and return the integer value of 2. In my case
am diving both numerator and denominator on the same line by the greatest common divisor. Although
it probably would be more efficient to create a local int variable and store GCD value in it, but
for such small program it shouldn't make any difference.*/
Fraction Sum(Fraction otherFraction)
{
int n = numerator*otherFraction.denominator+otherFraction.numerator*denominator;
int d = denominator*otherFraction.denominator;
return Fraction(n/gcd(n,d),d/gcd(n,d));
}
Fraction Difference(Fraction otherFraction)
{
int n = numerator*otherFraction.denominator-otherFraction.numerator*denominator;
int d = denominator*otherFraction.denominator;
return Fraction(n/gcd(n,d),d/gcd(n,d));
}
Fraction Product(Fraction otherFraction)
{
int n = numerator*otherFraction.numerator;
int d = denominator*otherFraction.denominator;
return Fraction(n/gcd(n,d),d/gcd(n,d));
}
Fraction Division(Fraction otherFraction)
{
int n = numerator*otherFraction.denominator;
int d = denominator*otherFraction.numerator;
return Fraction(n/gcd(n,d),d/gcd(n,d));
}
// I got the GCD algorithm from the following source:
// Source C#: http://www.ww.functionx.com/csharp2/examples/gcd.htm
int gcd(int n, int d)
{
int remainder;
while (d != 0)
{
remainder = n % d;
n = d;
d = remainder;
}
return n;
}
void show() // Display method
{
if (denominator == 1) // e.g. fraction 2/1 will display simply as 2
cout << numerator << endl;
else
cout << numerator << "/" << denominator << endl;
}
};
int main()
{
Fraction a(1,2);
Fraction b(1,4);
Fraction c;
c = a.Sum(b); // Result: 3/4
c.show();
c = a.Difference(b); // Result: 1/4
c.show();
c = a.Product(b); // Result: 1/8
c.show();
c = a.Division(b); // Result: 2
c.show();
return 0;
}
Upvotes: 5
Views: 69731
Reputation: 26
I created a version of this Fractions class, and although my coding is terrible, it should work. Note that I defined the <=>
operator, which is introduced in C++20, and defines <, >, <=, >=
automatically. If you don't have that, you could use std::rel_ops
or just defining them all one by one.
Fraction.h:
#include <memory>
#include <numeric>
#include <string>
class Fraction
{
private:
long long num{ 1 };
long long denom{ 1 };
inline void simplify() {
auto gcd{ std::make_unique<long long>(std::gcd(num, denom)) };
num /= *gcd;
denom /= *gcd;
gcd.reset();
if (denom < 0) {
num = -num;
denom = -denom;
}
}
public:
Fraction(double numerator = 1, double denominator = 1);
Fraction(double numerator, double denominator, int integer_part);
Fraction(const Fraction& f);
long long numerator() const;
long long denominator() const;
auto setnumerator(long long nv);
auto setdenominator(long long nv);
double value() const;
Fraction reciprocal() const;
std::string str(size_t mode = 0) const;
bool operator==(const Fraction& f) const;
auto operator<=>(const Fraction& f) const;
bool operator==(size_t s) const;
auto operator<=>(size_t s) const;
friend bool operator== (size_t s, Fraction& f);
friend auto operator<=> (size_t s, Fraction& f);
bool operator==(double d) const;
auto operator<=>(double d) const;
friend bool operator== (double d, Fraction& f);
friend auto operator<=> (double d, Fraction& f);
Fraction operator+(const Fraction& f) const;
Fraction operator+(size_t s) const;
Fraction operator+(double d) const;
friend Fraction operator+(size_t s, const Fraction& f);
friend Fraction operator+(double d, const Fraction& f);
Fraction operator-(const Fraction& f) const;
Fraction operator-(size_t s) const;
Fraction operator-(double d) const;
friend Fraction operator-(size_t s, const Fraction& f);
friend Fraction operator-(double d, const Fraction& f);
Fraction operator*(const Fraction& f) const;
Fraction operator*(size_t s) const;
Fraction operator*(double d) const;
friend Fraction operator*(size_t s, const Fraction& f);
friend Fraction operator*(double d, const Fraction& f);
Fraction operator/(const Fraction& f) const;
Fraction operator/(size_t s) const;
Fraction operator/(double d) const;
friend Fraction operator/(size_t s, const Fraction& f);
friend Fraction operator/(double d, const Fraction& f);
Fraction operator%(const Fraction& f) const;
Fraction operator%(size_t s) const;
Fraction operator%(double d) const;
friend size_t operator%(size_t s, const Fraction& f);
friend double operator%(double d, const Fraction& f);
Fraction& operator+=(const Fraction& f);
Fraction& operator+=(size_t s);
Fraction& operator+=(double d);
friend size_t operator+=(size_t s, Fraction& f);
friend double operator+=(double d, Fraction& f);
Fraction& operator-=(const Fraction& f);
Fraction& operator-=(size_t s);
Fraction& operator-=(double d);
friend size_t operator-=(size_t s, Fraction& f);
friend double operator-=(double d, Fraction& f);
Fraction& operator*=(const Fraction& f);
Fraction& operator*=(size_t s);
Fraction& operator*=(double d);
friend size_t operator*=(size_t s, Fraction& f);
friend double operator*=(double d, Fraction& f);
Fraction& operator/=(const Fraction& f);
Fraction& operator/=(size_t s);
Fraction& operator/=(double d);
friend size_t operator/=(size_t s, Fraction& f);
friend double operator/=(double d, Fraction& f);
Fraction& operator%=(const Fraction& f);
Fraction& operator%=(size_t s);
Fraction& operator%=(double d);
friend size_t operator%=(size_t s, Fraction& f);
friend double operator%=(double d, Fraction& f);
Fraction& operator++();
Fraction operator++(int);
Fraction& operator--();
Fraction operator--(int);
Fraction operator+();
Fraction operator-();
long long operator[](int i);
};
Fraction.cpp:
#include "Fraction.h"
#include <limits>
#include <sstream>
Fraction::Fraction(double numerator, double denominator)
{
double tsn{ numerator }, tsd{ denominator };
while ((static_cast<long long>(tsn) != tsn || static_cast<long long>(tsd) != tsd) && std::numeric_limits<long long>::max() / 10 > tsn && std::numeric_limits<long long>::max() / 10 > tsd) {
tsn *= 10;
tsd *= 10;
}
num = tsn;
denom = tsd;
simplify();
}
Fraction::Fraction(double numerator, double denominator, int integer_part)
{
numerator += denominator * integer_part;
double tsn{ numerator }, tsd{ denominator };
while ((static_cast<long long>(tsn) != tsn || static_cast<long long>(tsd) != tsd) && std::numeric_limits<long long>::max() / 10 > tsn && std::numeric_limits<long long>::max() / 10 > tsd) {
tsn *= 10;
tsd *= 10;
}
num = tsn;
denom = tsd;
simplify();
}
Fraction::Fraction(const Fraction& f)
{
num = f.numerator();
denom = f.denominator();
}
long long Fraction::numerator() const
{
return num;
}
long long Fraction::denominator() const
{
return denom;
}
auto Fraction::setnumerator(long long nv)
{
num = nv;
simplify();
return this;
}
auto Fraction::setdenominator(long long nv)
{
denom = nv;
simplify();
return this;
}
double Fraction::value() const
{
return static_cast<double>(num) / denom;
}
Fraction Fraction::reciprocal() const
{
return Fraction(denom, num);
}
std::string Fraction::str(size_t mode) const
{
std::stringstream ss;
if (mode == 0) {
ss << num;
if (denom != 1) {
ss << '/' << denom;
}
}
else {
if (num % denom == 0 || denom == 1) {
ss << num / denom;
}
else {
if (abs(denom) > abs(num)) {
ss << num << '/' << denom;
}
else if (num < 0) {
ss << '-' << '(' << (num / denom) << ' ' << -(num % denom) << '/' << denom << ')';
}
else {
ss << (num / denom) << ' ' << num % denom << '/' << denom;
}
}
}
return ss.str();
}
bool Fraction::operator==(const Fraction& f) const
{
return value() == f.value();
}
auto Fraction::operator<=>(const Fraction& f) const
{
return value() <=> f.value();
}
bool Fraction::operator==(size_t s) const
{
return value() == s;
}
auto Fraction::operator<=>(size_t s) const
{
return value() <=> s;
}
bool operator==(size_t s, const Fraction& f)
{
return s == f.value();
}
auto operator<=>(size_t s, const Fraction& f)
{
return s <=> f.value();
}
bool Fraction::operator==(double d) const
{
return value() == d;
}
auto Fraction::operator<=>(double d) const
{
return value() <=> d;
}
bool operator==(double d, Fraction& f)
{
return f.value() == d;
}
auto operator<=>(double d, Fraction& f)
{
return f.value() <=> d;
}
Fraction Fraction::operator+(const Fraction& f) const
{
return Fraction(num * f.denominator() + f.numerator() * denom, denom * f.denominator());
}
Fraction Fraction::operator+(size_t s) const
{
return Fraction(s * denom + num, denom);
}
Fraction Fraction::operator+(double d) const
{
return *this + Fraction(d);
}
Fraction operator+(size_t s, const Fraction& f)
{
return f + s;
}
Fraction operator+(double d, const Fraction& f)
{
return f + d;
}
Fraction Fraction::operator-(const Fraction& f) const
{
return Fraction(num * f.denominator() - f.numerator() * denom, denom * f.denominator());
}
Fraction Fraction::operator-(size_t s) const
{
return Fraction(num - (s * denom), denom);
}
Fraction Fraction::operator-(double d) const
{
return *this - Fraction(d);
}
Fraction operator-(size_t s, const Fraction& f)
{
return Fraction(s * f.denom - f.num, f.denom);
}
Fraction operator-(double d, const Fraction& f)
{
return Fraction(d) - f;
}
Fraction Fraction::operator*(const Fraction& f) const
{
return Fraction(num * f.numerator(), denom * f.denominator());
}
Fraction Fraction::operator*(size_t s) const
{
return Fraction(num * s, denom);
}
Fraction Fraction::operator*(double d) const
{
return Fraction(num * d, denom);
}
Fraction operator*(size_t s, const Fraction& f)
{
return Fraction(f.numerator() * s, f.denominator());
}
Fraction operator*(double d, const Fraction& f)
{
return Fraction(f.numerator() * d, f.denominator());
}
Fraction Fraction::operator/(const Fraction& f) const
{
return (*this) * f.reciprocal();
}
Fraction Fraction::operator/(size_t s) const
{
return Fraction(num, denom * s);
}
Fraction Fraction::operator/(double d) const
{
return Fraction(num, denom * d);
}
Fraction operator/(size_t s, const Fraction& f)
{
return s * f.reciprocal();
}
Fraction operator/(double d, const Fraction& f)
{
return d * f.reciprocal();
}
Fraction Fraction::operator%(const Fraction& f) const
{
return ((*this) / f).value() > 0 ? Fraction((*this) - (floor(((*this) / f).value()) * f)) : Fraction((*this) - (ceil(((*this) / f).value()) * f));
}
Fraction Fraction::operator%(size_t s) const
{
return ((*this) / s).value() > 0 ? Fraction((*this) - (floor(((*this) / s).value()) * s)) : Fraction((*this) - (ceil(((*this) / s).value()) * s));
}
Fraction Fraction::operator%(double d) const
{
return ((*this) / d).value() > 0 ? Fraction((*this) - (floor(((*this) / d).value()) * d)) : Fraction((*this) - (ceil(((*this) / d).value()) * d));
}
size_t operator%(size_t s, const Fraction& f)
{
return (f / s).value() > 0 ? ((f) - (floor((s / f).value()) * f)).value() : ((f)-(ceil((s / f).value()) * f)).value();
}
double operator%(double d, const Fraction& f)
{
return (f / d).value() > 0 ? ((f)-(floor((d / f).value()) * f)).value() : ((f)-(ceil((d / f).value()) * f)).value();
}
Fraction& Fraction::operator+=(const Fraction& f)
{
*this = *this + f;
return *this;
}
Fraction& Fraction::operator+=(size_t s)
{
*this = *this + s;
return *this;
}
Fraction& Fraction::operator+=(double d)
{
*this = *this + d;
return *this;
}
size_t operator+=(size_t s, Fraction& f)
{
s += round(f.value());
return s;
}
double operator+=(double d, Fraction& f)
{
d += f.value();
return d;
}
Fraction& Fraction::operator-=(const Fraction& f)
{
*this = *this - f;
return *this;
}
Fraction& Fraction::operator-=(size_t s)
{
*this = *this - s;
return *this;
}
Fraction& Fraction::operator-=(double d)
{
*this = *this - d;
return *this;
}
size_t operator-=(size_t s, Fraction& f)
{
s -= round(f.value());
return s;
}
double operator-=(double d, Fraction& f)
{
d -= f.value();
return d;
}
Fraction& Fraction::operator*=(const Fraction& f)
{
*this = *this * f;
return *this;
}
Fraction& Fraction::operator*=(size_t s)
{
*this = *this * s;
return *this;
}
Fraction& Fraction::operator*=(double d)
{
*this = *this * d;
return *this;
}
size_t operator*=(size_t s, Fraction& f)
{
s *= round(f.value());
return s;
}
double operator*=(double d, Fraction& f)
{
d *= f.value();
return d;
}
Fraction& Fraction::operator/=(const Fraction& f)
{
*this = *this / f;
return *this;
}
Fraction& Fraction::operator/=(size_t s)
{
*this = *this / s;
return *this;
}
Fraction& Fraction::operator/=(double d)
{
*this = *this / d;
return *this;
}
size_t operator/=(size_t s, Fraction& f)
{
s /= round(f.value());
return s;
}
double operator/=(double d, Fraction& f)
{
d /= f.value();
return d;
}
Fraction& Fraction::operator%=(const Fraction& f)
{
*this = *this % f;
return *this;
}
Fraction& Fraction::operator%=(size_t s)
{
*this = *this % s;
return *this;
}
Fraction& Fraction::operator%=(double d)
{
*this = *this % d;
return *this;
}
size_t operator%=(size_t s, Fraction& f)
{
s = s % f;
return s;
}
double operator%=(double d, Fraction& f)
{
d = d % f;
return d;
}
Fraction& Fraction::operator++()
{
this->num += this->denom;
return *this;
}
Fraction Fraction::operator++(int)
{
Fraction copy{ *this };
this->num += this->denom;
return copy;
}
Fraction& Fraction::operator--()
{
this->num -= this->denom;
return *this;
}
Fraction Fraction::operator--(int)
{
Fraction copy{ *this };
this->num -= this->denom;
return copy;
}
Fraction Fraction::operator+()
{
return *this;
}
Fraction Fraction::operator-()
{
return Fraction(-num, denom);
}
long long Fraction::operator[](int i)
{
return i == 0 ? num : denom;
}
Most of the bottom ones are just operator overloads of different types as I was going to use the operators on Fraction objects, size_t and double objects. This should be more optimized as it has a simplify() function that is used and instead of functions like sum and difference, I used operator overloading. Fraction.str() is a bit more optimized than above. As for the zero division thing, I think that it will get caught and the compiler will at least issue a warning and when running, it should give a floating point error. If you found any bugs in the code, please comment below!
Upvotes: 0
Reputation: 1
Im a new guy so im sorry if my code looks so noob :P
#include<iostream>
using namespace std;
class Fraction
{
private :
int x;//NUMERATOR
int y;//DENOMINATOR
public :
Fraction(int tx = 0, int ty = 1)
{
if(ty == 0)
{
x = 0;
y = 1;
exit(0);
}else
{
x = tx;
y = ty;
}
}
void simplify()
{
for(int i = x; i >1;i--)
{
if(x%i == 0 && y%i == 0)
{
x = x/i;
y = y/i;
}
}
}
void Input()
{
cout<<"Input NUMERATOR: ";
cin>>x;
cout<<"Input DENOMINATOR: ";
cin>>y;
if(y==0)
{
cout<<"ERROR";
exit(0);
}
}
void Display()
{
cout<< x << "/" << y <<endl;
}
Fraction operator + (Fraction f){
Fraction temp;
temp.x = x * f.y + y * f.x;
temp.y = y * f.y;
return temp;
}
Fraction operator - (Fraction f){
Fraction temp;
temp.x = x * f.y - y * f.x;
temp.y = y * f.y;
return temp;
}
Fraction operator * (Fraction f){
Fraction temp;
temp.x = x * f.x ;
temp.y = y * f.y;
return temp;
}
Fraction operator / (Fraction f){
Fraction temp;
temp.x = x * f.y ;
temp.y = y * f.x;
return temp;
}
};
int main ()
{
Fraction f1;
f1.Input();
cout<<"Fraction: ";
f1.simplify();
f1.Display();
Fraction f2;
f2.Input();
cout<<"Fraction: ";
f2.simplify();
f2.Display();
Fraction f3,f4,f5,f6;
f3 = f1 + f2;
f4 = f1 - f2;
f5 = f1 * f2;
f6 = f1 / f2;
cout<<"Sum"<<endl;
f3.simplify();
f3.Display();
cout<<"Difference"<<endl;
f4.simplify();
f4.Display();
cout<<"Product"<<endl;
f5.simplify();
f5.Display();
cout<<"Division"<<endl;
f6.simplify();
f6.Display();
return 0;
}
Blockquote
Upvotes: 0
Reputation: 141790
So many questions in one question! Let me answer one (pair) for you:
Why does book show fraction equivalences? What do I have to do with that?
Consider this usage of your Fraction
class:
TEST(FractionsAreEquivalent)
{
const Fraction one_over_two(1, 2); // 0.5
const Fraction two_over_four(2, 4); // 0.5
const bool equivalent = (one_over_two == two_over_four);
CHECK(equivalent);
}
This uses bool Fraction::operator ==(const Fraction& rhs) const
.
Upvotes: 1
Reputation: 76898
Your methods could be (assuming your class is named "Fraction"):
Fraction Fraction::sum(Fraction &otherFraction)
Fraction Fraction::difference(Fraction &otherFraction)
Fraction Fraction::product(Fraction &otherFraction)
Fraction Fraction::division(Fraction &otherFraction)
(Using the method names you described - I might go for something different)
Each of these would return a new Fraction
object containing the result.
You'd be into bonus points for using operator overloading and re-defining what + - / *
meant.
Upvotes: 1
Reputation: 41858
You may want to start with just the first part, create the class with two constructors, one if they give the two numbers, and one that defaults to 1
.
Then, write the four functions, the assignment is nice enough to give you the equations.
If a denominator is ever equal to zero then I would throw an exception, as that is something the user may be able to fix. If you can show what led to it being zero, for example, if they divide (1/2)/(0/4) then your denominator is zero, which should be an error condition.
One issue you will want to look at is if this will be immutable, so, if I have (1/2) and (2/3) and I do an operation, it should return a new number, not modify either of the two that I passed in. In that case your check is always in the constructor for a zero, but you should explain which operation led to this error.
Upvotes: 2
Reputation: 33386
Your fraction constructor should take a numerator and a denominator as parameters (or a whole number). The entire problem could be solved easily with operator overloading.
And yes, the best way to handle failed constructors is via exceptions. Don't forget to throw the same division_by_zero
exception when you're dividing two fractions and the numerator of the denominator is zero.
Upvotes: 2
Reputation: 3642
Also what would be a good way to prohibit denominator of 0? Throw exception or force it to be equal to 1?
That's completely a style question, with no provably correct answer...
But... I'd go with an exception. Again, this is just style. Changing the value silently would be carrying on silently forward having done something other than what the caller asked for.
Another alternative would be to allow 0 denominator, but have a way to inform the user that the current overall value, a/b, is NaN or infinity, or whatever "special value" you care to define.
In any case, document it carefully, so a user of the class can make informed choices.
Upvotes: 2