patterncppMinor
Rational arithmetic from given input
Viewed 0 times
rationalinputfromgivenarithmetic
Problem
I'm currently working on this for my own practice. I get 4 digits and an operation.
The input is
To summarize what I do:
Here is the main function and how I handle if the operation is
```
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
long long nwd(long long a, long long b){
long long c;
while(b != 0){
c = a % b;
a = b;
b = c;
}
return a;
}
void add(long long x1, long long y1, long long x2, long long y2){
long long bottom = (y1) * (y2);
long long top = ((x1) (y2)) + ((x2) (y1));
//cout << bottom << " " << top << endl;
long long frac;
if(bottom != 0||top != 0){
frac = nwd(top,bottom);
}else{
frac = 1;
}
string sign = "";
if(top * bottom < 0){
sign = "-";
}else{
sign = "";
}
printf("%s%lld / %lld\n",sign.c_str(),abs(top/frac),abs(bottom/frac) );
}
void sub(long long x1, long long y1,long long x2, long long y2){
long long bottom = (y1) * (y2);
long long top = ((x1) (y2)) - ((x2) (y1));
long long frac;
if(bottom != 0||top != 0){
frac = nwd(top,bottom);
}else{
frac
The input is
x1 y1 op x2 y2 and the fraction is x1/y1 and x2/y2. If I get the input: 1 3 + 1 2 then it's 1/3 + 1/2 and the answer should be given the minimal fractional so it's 5/6. I pass the testcases I get and I can't figure out what I'm doing wrong.To summarize what I do:
- Read input and check the operation if it's
+,-,/or*. I generate a prime array to find the biggest common divisor.
- Send the input to a function depending on which operation it is.
- I count the given input with simple math.
- Then I find the biggest common divisor and divide both numerator and denominator with this.
- After that I print out the result.
Here is the main function and how I handle if the operation is
*. I handle the other operation the same but with other math.```
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
long long nwd(long long a, long long b){
long long c;
while(b != 0){
c = a % b;
a = b;
b = c;
}
return a;
}
void add(long long x1, long long y1, long long x2, long long y2){
long long bottom = (y1) * (y2);
long long top = ((x1) (y2)) + ((x2) (y1));
//cout << bottom << " " << top << endl;
long long frac;
if(bottom != 0||top != 0){
frac = nwd(top,bottom);
}else{
frac = 1;
}
string sign = "";
if(top * bottom < 0){
sign = "-";
}else{
sign = "";
}
printf("%s%lld / %lld\n",sign.c_str(),abs(top/frac),abs(bottom/frac) );
}
void sub(long long x1, long long y1,long long x2, long long y2){
long long bottom = (y1) * (y2);
long long top = ((x1) (y2)) - ((x2) (y1));
long long frac;
if(bottom != 0||top != 0){
frac = nwd(top,bottom);
}else{
frac
Solution
I see a number of things that may help you improve your code.
Use only required
The code has a great number of
Don't Repeat Yourself (DRY)
The mathematical operations all include large portions of repeated code. Instead of repeating code, it's generally better to make common code into a function.
Don't abuse
Putting
Use objects
Since you're writing in C++, what would make more sense to me would be to create a rational fraction object. Then each mathematical operation would be very naturally expressed as an operator on the object.
Use
The more appropriate control flow for your
Prefer
Using
Omit
When a C++ program reaches the end of
Putting it all together
Here's an alternative implementation which uses all of these suggestions. Note that a rational fraction is only reduced when it is being printed. This saves some time over always maintaining that state.
A bit more detail
To understand this version of the code, it's useful to look at a few individual class member functions. First, is the constructor:
This allows for construction in a few different ways:
The basic four mathematical functions are all built in the same way. First, there is a member function such as
This version is a member function and can be used like this:
It's a common idiom to define those member functions first and then construct the two-operand versions:
This is a bit subtle. Note that
Use only required
#includesThe code has a great number of
#includes that are not needed. Some files are even listed twice. This clutters the code and makes it more difficult to read and understand. Only include files that are actually needed.Don't Repeat Yourself (DRY)
The mathematical operations all include large portions of repeated code. Instead of repeating code, it's generally better to make common code into a function.
Don't abuse
using namespace stdPutting
using namespace std at the top of every program is a bad habit that you'd do well to avoid. Use objects
Since you're writing in C++, what would make more sense to me would be to create a rational fraction object. Then each mathematical operation would be very naturally expressed as an operator on the object.
Use
switch instead of nested ifThe more appropriate control flow for your
if statement in main is a switch statement. This makes it somewhat clearer to read and understand and often provides a small improvement in performance, especially if there are many cases.Prefer
iostream to stdio.hUsing
iostream style I/O makes the code easier to adapt to change. For example, using scanf, the code has to have the appropriate matching format string for each argument, but using the >> operator, there is no such requirement and the compiler adapts automatically to whatever type is being read.Omit
return 0When a C++ program reaches the end of
main the compiler will automatically generate code to return 0, so there is no reason to put return 0; explicitly at the end of main.Putting it all together
Here's an alternative implementation which uses all of these suggestions. Note that a rational fraction is only reduced when it is being printed. This saves some time over always maintaining that state.
#include
class RationalFraction
{
public:
RationalFraction(long long a=0, long long b=1)
: num{a}, den{b}
{ }
// unary negation
RationalFraction& operator-() { num = -num; return *this; }
// add the passed fraction to this one
// The two-operand addition is built from this, and a
// similar pattern is used for each of the other operators.
RationalFraction& operator+=(const RationalFraction &rh) {
num = num * rh.den + rh.num *den;
den *= rh.den;
return *this;
}
RationalFraction& operator-=(const RationalFraction &rh) {
num = num * rh.den - rh.num *den;
den *= rh.den;
return *this;
}
RationalFraction& operator*=(const RationalFraction &rh) {
num *= rh.num;
den *= rh.den;
return *this;
}
RationalFraction& operator/=(const RationalFraction &rh) {
num *= rh.den;
den *= rh.num;
return *this;
}
friend std::ostream &operator> (std::istream& in, RationalFraction& f) {
return in >> f.num >> f.den;
}
private:
void reduce() {
if (num == 0) {
den = 1;
return;
}
long long mygcd = gcd(num, den);
num /= mygcd;
den /= mygcd;
if (den > numOp; numOp; --numOp) {
RationalFraction lhs, rhs;
char op[2];
std::cin >> lhs >> op >> rhs;
switch (op[0]) {
case '+':
std::cout << lhs+rhs << '\n';
break;
case '-':
std::cout << lhs-rhs << '\n';
break;
case '/':
std::cout << lhs/rhs << '\n';
break;
default:
std::cout << lhs*rhs << '\n';
}
}
}A bit more detail
To understand this version of the code, it's useful to look at a few individual class member functions. First, is the constructor:
RationalFraction(long long a=0, long long b=1)
: num{a}, den{b}
{ }This allows for construction in a few different ways:
RationalFraction half{1,2}; // 1/2 = one half
RationalFraction three{3}; // 3/1 = 3
RationalFraction zero; // 0/1 = 0The basic four mathematical functions are all built in the same way. First, there is a member function such as
+=:RationalFraction& operator+=(const RationalFraction &rh) {
num = num * rh.den + rh.num *den;
den *= rh.den;
return *this;
}This version is a member function and can be used like this:
RationalFraction a{1,3};
RationalFraction b{1,2};
a += b; // now a = 5/6 and b is unchangedIt's a common idiom to define those member functions first and then construct the two-operand versions:
RationalFraction operator+(RationalFraction lh, const RationalFraction &rh)
{
lh += rh;
return lh;
}This is a bit subtle. Note that
lh is passed by value, so that means a local copy is constructed using the compiler-generated default copy. By contrast rh is a constCode Snippets
#include <iostream>
class RationalFraction
{
public:
RationalFraction(long long a=0, long long b=1)
: num{a}, den{b}
{ }
// unary negation
RationalFraction& operator-() { num = -num; return *this; }
// add the passed fraction to this one
// The two-operand addition is built from this, and a
// similar pattern is used for each of the other operators.
RationalFraction& operator+=(const RationalFraction &rh) {
num = num * rh.den + rh.num *den;
den *= rh.den;
return *this;
}
RationalFraction& operator-=(const RationalFraction &rh) {
num = num * rh.den - rh.num *den;
den *= rh.den;
return *this;
}
RationalFraction& operator*=(const RationalFraction &rh) {
num *= rh.num;
den *= rh.den;
return *this;
}
RationalFraction& operator/=(const RationalFraction &rh) {
num *= rh.den;
den *= rh.num;
return *this;
}
friend std::ostream &operator<< (std::ostream& out, const RationalFraction& f) {
RationalFraction fr=f;
fr.reduce();
return out << fr.num << " / " << fr.den;
}
friend std::istream &operator>> (std::istream& in, RationalFraction& f) {
return in >> f.num >> f.den;
}
private:
void reduce() {
if (num == 0) {
den = 1;
return;
}
long long mygcd = gcd(num, den);
num /= mygcd;
den /= mygcd;
if (den < 0) {
den = -den;
num = -num;
}
}
long long gcd(long long a, long long b) const {
long long c;
while(b != 0){
c = a % b;
a = b;
b = c;
}
return a;
}
// data members
long long num, den;
};
RationalFraction operator+(RationalFraction lh, const RationalFraction &rh)
{
lh += rh;
return lh;
}
RationalFraction operator-(RationalFraction lh, const RationalFraction &rh)
{
lh -= rh;
return lh;
}
RationalFraction operator*(RationalFraction lh, const RationalFraction &rh)
{
lh *= rh;
return lh;
}
RationalFraction operator/(RationalFraction lh, const RationalFraction &rh)
{
lh /= rh;
return lh;
}
int main()
{
int numOp;
for (std::cin >> numOp; numOp; --numOp) {
RationalFraction lhs, rhs;
char op[2];
std::cin >> lhs >> op >> rhs;
switch (op[0]) {
case '+':
std::cout << lhs+rhs << '\n';
break;
case '-':
std::cout << lhs-rhs << '\n';
break;
case '/':
std::cout << lhs/rhs << '\n';
break;
default:
std::cout << lhs*rhs << '\n';
}
}
}RationalFraction(long long a=0, long long b=1)
: num{a}, den{b}
{ }RationalFraction half{1,2}; // 1/2 = one half
RationalFraction three{3}; // 3/1 = 3
RationalFraction zero; // 0/1 = 0RationalFraction& operator+=(const RationalFraction &rh) {
num = num * rh.den + rh.num *den;
den *= rh.den;
return *this;
}RationalFraction a{1,3};
RationalFraction b{1,2};
a += b; // now a = 5/6 and b is unchangedContext
StackExchange Code Review Q#99098, answer score: 3
Revisions (0)
No revisions yet.