Fraction Calculator

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
User avatar
Maevik
Chaos Rift Junior
Chaos Rift Junior
Posts: 230
Joined: Mon Mar 02, 2009 3:22 pm
Current Project: www.keedepictions.com/Pewpew/
Favorite Gaming Platforms: PC
Programming Language of Choice: C++
Location: Long Beach, CA

Fraction Calculator

Post by Maevik »

So I just finished a console project that took me most of the day. I haven't had any feedback on any of my programming and I've been doing it for a few months now. I was wondering if anyone wouldn't mind looking at my code and let me know if there are any glaring mistakes with how I'm going about doing some of this stuff. I realize it's a lot to ask, and if nobody is up to the task I understand. Also, feel free to just play around with the program if you dont feel like looking over the code, it's pretty neat.

Code: Select all

/*  Fraction Calculator
	Author: Chris Kee
	March 28, 2009
*/

#include <iostream>
#include <string>
#include <cmath>
#include <iomanip>
#include <fstream>

using namespace std;

enum operation { PLUS , MINUS , MULT , DIV };

struct fraction
{
	int numer;
	int denom;
};

void drawScreen( fraction x , fraction y , fraction z , operation n );
int noOfDigs( int x );
void makeSpace( int x );
void handleInput( istream& cin , fraction& x , fraction& y , fraction& z , operation& n , bool& quit );
void solveFraction( fraction& x , fraction& y , fraction& z , operation n );
void lcd( fraction& x );
int main()
{
	fraction num1 = { 0 , 1 };
	fraction num2 = { 0 , 1 };
	fraction answ = { 0 , 1 };
	operation oper = PLUS;	
	bool quit = false;

	while( !quit )
	{
		system("cls");		
		drawScreen( num1 , num2 , answ , oper );
		handleInput( cin , num1 , num2 , answ , oper , quit );
	}
}
void drawScreen( fraction x , fraction y , fraction z , operation n )
{
	//int variable to hold number of spaces we need;
	int sp = 0;

	cout << endl;
	if( ( x.denom == 0 ) || ( y.denom == 0 ) || ( z.denom == 0 ) )
	{
		cout << "\t\tERROR: Undefined Results! Denominator Cannot Be Zero";
	}
	cout << endl;

	//Draw top border
	for( int i = 0 ; i < 80 ; i++ )
		cout << '~';

	cout << endl << endl;

	//First fraction holds 10 horizontal spaces, the operator holds 5, 1 space buffer in between things.	
	

	//****************************************  DRAW THE NUMERATORS  ****************************************************

	//Make 15 spaces ( margin )
	makeSpace( 15 );
	
	//Width of numerators is 10, we will append spaces to the end until the number is centered.
	sp = noOfDigs( x.numer );
	cout << setw( 5 + ( sp / 2  ) ) << x.numer;
	makeSpace( 10 - ( 5 + ( sp / 2 ) ) );

	//Room to the next Numerator.  There will be, this is where the operator will be, but we're above that now.
	makeSpace( 7 );

	//Width of numerators is 10, we will append spaces to the end until the number is centered.
	sp = noOfDigs( y.numer );
	cout << setw( 5 + ( sp / 2 ) ) << y.numer;
	makeSpace( 10 - ( 5 + ( sp / 2 ) ) );

	//Room to the next Numerator.  There will be, this is where the operator will be, but we're above that now.
	makeSpace( 7 );

	//Width of numerators is 10, we will append spaces to the end until the number is centered.
	sp = noOfDigs( z.numer );
	cout << setw( 5 + ( sp / 2 ) ) << z.numer;
	makeSpace( 10 - ( 5 + ( sp / 2 ) ) );

	//Skip two spaces, ease of reading;
	cout << endl << endl;






	//**************************  NEXT SECTION BUILDS OPERATORS AND DIVISION LINES!!!  **********************************

	//margin
	makeSpace( 14 );
	
	//Draw x's division line
	for( int i = 0 ; i < 10 ; i++ )
		cout << '=';

	//Draw the operator
	if( n == PLUS )
	{
		cout << "   +   ";
	}else if( n == MINUS )
	{
		cout << "   -   ";
	}else if( n == MULT )
	{
		cout << "   *   ";
	}else if( n == DIV )
	{
		cout << "   /   ";
	}

	//Draw y's division line
	for( int i = 0 ; i < 10 ; i++ )
		cout << '=';

	//Draw equal sign
	cout << "   =   ";

	//Draw z's division line
	for( int i = 0 ; i < 10 ; i++ )
		cout << '=';

	//Skip two spaces for ease of reading
	cout << endl << endl;





	//**********************************  DRAW THE DENOMINATORS  *******************************************************

	//Make 15 spaces ( margin )
	makeSpace( 15 );
	
	//Width of denominators is 10, we will append spaces to the end until the number is centered.
	sp = noOfDigs( x.denom );
	cout << setw( 5 + ( sp / 2  ) ) << x.denom;
	makeSpace( 10 - ( 5 + ( sp / 2 ) ) );

	//Room to the next denomator.  There will be, this is where the operator will be, but we're above that now.
	makeSpace( 7 );

	//Width of denominators is 10, we will append spaces to the end until the number is centered.
	sp = noOfDigs( y.denom );
	cout << setw( 5 + ( sp / 2 ) ) << y.denom;
	makeSpace( 10 - ( 5 + ( sp / 2 ) ) );

	//Room to the next denominator.  There will be, this is where the operator will be, but we're above that now.
	makeSpace( 7 );

	//Width of denomators is 10, we will append spaces to the end until the number is centered.
	sp = noOfDigs( z.denom );
	cout << setw( 5 + ( sp / 2 ) ) << z.denom;
	makeSpace( 10 - ( 5 + ( sp / 2 ) ) );

	//Skip two spaces, ease of reading;
	cout << endl << endl;

	//Draw bottom border
	cout << endl << endl;
	for( int i = 0 ; i < 80 ; i++ )
		cout << '~';



}
int noOfDigs( int x )
{
	//This function takes in an integer, and returns the number of digits that integer has.
	//IE: the integer 6453 has 4 digits, so noOfDigs( 6453 ) returns 4.

	int digs = 0;

	for( int i = 0 ; pow( 10.0 , static_cast<double>( i ) ) < x ; i++ )
	{
		digs = i;
	}

	return digs;
}
void makeSpace( int x )
{
	for( int i = 0 ; i < x ; i++ )
		cout << ' ';
}
void handleInput( istream& cin , fraction& x , fraction& y , fraction& z , operation& n , bool& quit )
{
	/*
		This function both creates a menu and takes input from the user.  The input it takes usually a number
		we can use to navigate the menu.  It will also prompt the user for an integer when appropriate.  IE To
		use as a numerator or a denominator for the selecte fraction
	*/

	//Variable used to take input into the menu so we can navigate.
	int input = 0;

	//This static variable keeps track of where we are in the menu.
	static int loc = 0;

	//Instructional
	cout << endl << "\tPlease Enter An Option" << endl << endl;

	//Build the Main menu
	if( loc == 0 )
	{
		cout << "\t1: Enter a Numerator\t2: Enter a Denominator\n\t3: Choose Operation\t4: Solve\n\t5: Quit" << endl << endl;
	}

	//Build Numerator Menu
	if( loc == 1 )
	{
		cout << "\t1: Enter numerator for fraction A\n\t2: Enter numerator for fraction B\n\t3: Back" << endl << endl;
	}

	//Build Denominator Menu
	if( loc == 2 )
	{
		cout << "\t1: Enter denominator for fraction A\n\t2: Enter denominator for fraction B\n\t3: Back" << endl << endl;
	}

	//Build Choose Operation Menu
	if( loc == 3 )
	{
		cout << "\t1: Addition\t\t2: Subtraction\n\t3: Multiplication\t4: Division\n\t5: Back" << endl << endl;
	}

	cout << "\n\t:";
	cin >> input;

	if( !cin )
	{
		cin.clear();
		cin.ignore( 1000 , '\n' );
	}

	if( loc == 0 )
	{
		switch( input )
		{
		case 1:
			loc = 1;
			break;
		case 2:
			loc = 2;
			break;
		case 3:
			loc = 3;
			break;
		case 4:
			solveFraction( x , y , z , n );
			break;
		case 5:
			quit = true;
			break;
		default:
			loc = 0;
		}
	}else if( loc == 1 )
	{
		switch( input )
		{
		case 1:
			cout << "Enter a value: ";
			cin >> x.numer;
			loc = 0;
			break;
		case 2:
			cout << "Enter a value: ";
			cin >> y.numer;
			loc = 0;
			break;
		case 3:
			loc = 0;
			break;
		default:
			loc = 1;
		}
	}else if( loc == 2 )
	{
		switch( input )
		{
		case 1:
			cout << "Enter a value: ";
			cin >> x.denom;
			loc = 0;
			break;
		case 2:
			cout << "Enter a value: ";
			cin >> y.denom;
			loc = 0;
			break;
		case 3:
			loc = 0;
			break;
		default:
			loc = 2;
		}
	}else if( loc == 3 )
	{
		switch( input )
		{
		case 1:
			n = PLUS;
			loc = 0;
			break;
		case 2:
			n = MINUS;
			loc = 0;
			break;
		case 3:
			n = MULT;
			loc = 0;
			break;
		case 4:
			n = DIV;
			loc = 0;
			break;
		case 5:
			loc = 0;
			break;
		default:
			loc = 3;
		}
	}
}

void solveFraction( fraction& x , fraction& y , fraction& z , operation n )
{
	//This function takes as input, three fractions and an operation.  It performs the selected operation
	//on x and y, and stores the result in z.
	//All fraction parameters are taken as references so they may be passed to the LCD function.
	//First we need to know what type of operation we are performing.  That is the first if else switch series

	//Make an output txt, I was using this to debug some problems with the math.
	//ofstream fout;

	//fout.open( "math.txt" , fstream::app );

	if( n == PLUS )
	{
		//fout << "\nPLUS:" << endl;

		//If the two fractions do not have the same denominator
		if( x.denom != y.denom )
		{
			int xden = x.denom;
			int yden = y.denom;

			//Multiply each fraction by the other term's denominator, this will give both fractions the same denominator
			x.denom *= yden;
			//fout << "x.denom *= y.denom; //" << x.denom << " " << y.denom << endl;

			x.numer *= yden;
			//fout << "x.numer *= y.denom; //" << x.numer << " " << y.denom << endl;


			y.denom *= xden;
			//fout << "y.denom *= x.denom; //" << y.denom << " " << x.denom << endl;

			y.numer *= xden;
			//fout << "y.numer *= x.denom; //" << y.numer << " " << x.denom << endl;
		}

		//Now add the numerators and preserve the common denominator.
		//fout << x.numer << "/" << x.denom << " + " << y.numer << "/" << y.denom << " = ";

		z.numer = x.numer + y.numer;
		z.denom = x.denom;

		//fout << z.numer << "/" << z.denom << endl;


	}else if( n == MINUS )
	{
		//Repeat the addition logic for subtraction.

		//fout << "\nMINUS:" << endl;

		//If the two fractions do not have the same denominator
		if( x.denom != y.denom )
		{
			int xden = x.denom;
			int yden = y.denom;

			//Multiply each fraction by the other term's denominator, this will give both fractions the same denominator
			x.denom *= yden;
			//fout << "x.denom *= y.denom; //" << x.denom << " " << y.denom << endl;

			x.numer *= yden;
			//fout << "x.numer *= y.denom; //" << x.numer << " " << y.denom << endl;


			y.denom *= xden;
			//fout << "y.denom *= x.denom; //" << y.denom << " " << x.denom << endl;

			y.numer *= xden;
			//fout << "y.numer *= x.denom; //" << y.numer << " " << x.denom << endl;
		}

		//Now add the numerators and preserve the common denominator.
		//fout << x.numer << "/" << x.denom << " - " << y.numer << "/" << y.denom << " = ";

		z.numer = x.numer - y.numer;
		z.denom = x.denom;

		//fout << z.numer << "/" << z.denom << endl;

	}else if( n == MULT )
	{
		//For multiplication, we simply multiply the numerators and denominators
		z.numer = x.numer * y.numer;
		z.denom = x.denom * y.denom;
	}else if( n == DIV )
	{
		//fout << "/nDIVISION" << endl;
		//For division, we multiply x by the inverse of y
		z.numer = x.numer * y.denom;
		//fout << "z.numer = x.numer * y.denom;   //" << z.numer << x.numer << y.denom << endl;

		z.denom = x.denom * y.numer;
		//fout << "z.denom = x.numer * y.numer;   //" << z.denom << x.numer << y.denom << endl;

	}

	//Put all fractions in simplest terms
	lcd( x );
	lcd( y );
	lcd( z );
}
void lcd( fraction& x )
{
	//This function takes the parameter fraction and puts in simplest terms.

	//And integer to hold our largest common factor.  
	int y = 1;

	//Find the factors of the numerator
	for( int i = 2 ; i <= x.numer ; i++ )
	{
		//If the numerator is evenly divisible by i
		if( x.numer % i == 0 )
		{
			//Check to see if it is also divisible by the denominator
			if( x.denom % i == 0 )
			{
				//If it is our largest common factor so far
				if( i >= y )
				{
					//Store it in a variable
					y = i;
				}
			}
		}
	}

	//Devide the fraction by the largest common factor	
	x.numer /= y;
	x.denom /= y;

	//If both sides of the division bar are negative, the fraction is positive.
	if( ( x.numer < 0 ) && ( x.denom < 0 ) )
	{
		x.numer = -x.numer;
		x.denom = -x.denom;
	}

	//If the fraction IS negative, make sure the negative sign is on the numerator
	if( ( x.numer > 0 ) && ( x.denom < 0 ) )
	{
		x.numer = -x.numer;
		x.denom = -x.denom;
	}
}
Attachments
FracCalc.zip
(9.17 KiB) Downloaded 50 times
My love is like a Haddoken, it's downright fierce!
User avatar
Ginto8
ES Beta Backer
ES Beta Backer
Posts: 1064
Joined: Tue Jan 06, 2009 4:12 pm
Programming Language of Choice: C/C++, Java

Re: Fraction Calculator

Post by Ginto8 »

Pretty cool program! Haven't really mucked around in the code - just skimmed - but it seems to work as it should! ;) :mrgreen: Nice job!
Quit procrastinating and make something awesome.
Ducky wrote:Give a man some wood, he'll be warm for the night. Put him on fire and he'll be warm for the rest of his life.
User avatar
Maevik
Chaos Rift Junior
Chaos Rift Junior
Posts: 230
Joined: Mon Mar 02, 2009 3:22 pm
Current Project: www.keedepictions.com/Pewpew/
Favorite Gaming Platforms: PC
Programming Language of Choice: C++
Location: Long Beach, CA

Re: Fraction Calculator

Post by Maevik »

Thanks!
I've been trying to practice good commenting, so hopefully it's easy to read.
My love is like a Haddoken, it's downright fierce!
User avatar
MarauderIIC
Respected Programmer
Respected Programmer
Posts: 3406
Joined: Sat Jul 10, 2004 3:05 pm
Location: Maryland, USA

Re: Fraction Calculator

Post by MarauderIIC »

It looks like you literally copy-pasted the draw the numerator/draw the denominator. Perhaps you should make it one function and pass in the # to draw.
Stylistically, either do

Code: Select all

    }
    else if(condition)
    {
or

Code: Select all

} else if (condition) {
I really don't recommend the style you use in your elses.

Use named constants. For instance, in your new "draw fraction part" function (where you recycle the same code to draw a numerator and a denominator),

Code: Select all

const int SPACES = 10
or something like that, and then use that to print your surrounding spaces.

Also, when it comes to writing a particular thing using a particular width, I would use printf() :

Code: Select all

#include <cstdio>
using namespace std;
...
int myNum = 2;
printf("%5d", myNum);
will left-justify myNum in a 5 character wide field.

To center it in an arbitrary length field, this would probably work:

Code: Select all

string space = " ";
int myNum = 2;
const int SPACE_CONST = 5;
printf("%*d%*s", SPACE_CONST/2, myNum, SPACE_CONST/2, space.c_str());
.
Which makes it so SPACE_CONST/2 replaces *, myNum replaces d, SPACE_CONST/2 replaces *, and space replaces s. Much less awkward than cout's setw(). But actually that wouldn't center it. You could use the same algorithm you're using right now, probably, actually.
http://www.cplusplus.com/reference/clib ... rintf.html
I realized the moment I fell into the fissure that the book would not be destroyed as I had planned.
User avatar
Maevik
Chaos Rift Junior
Chaos Rift Junior
Posts: 230
Joined: Mon Mar 02, 2009 3:22 pm
Current Project: www.keedepictions.com/Pewpew/
Favorite Gaming Platforms: PC
Programming Language of Choice: C++
Location: Long Beach, CA

Re: Fraction Calculator

Post by Maevik »

the
}else if( stuff )
format I was using is the way the way it's presented in my C++ book. I simply thought that was the syntax. Is there a specific reason not to do it?

Thanks for the tip on printf(), it's not (yet?) been covered in my book or my class. I'm assuming it's something from C?
My love is like a Haddoken, it's downright fierce!
User avatar
MarauderIIC
Respected Programmer
Respected Programmer
Posts: 3406
Joined: Sat Jul 10, 2004 3:05 pm
Location: Maryland, USA

Re: Fraction Calculator

Post by MarauderIIC »

Just a prettiness reason. I'd never seen it done } else if ( ... ) \n {, honestly, and didn't care for it much :)

printf() is C, yes.
I realized the moment I fell into the fissure that the book would not be destroyed as I had planned.
Post Reply