Page 1 of 1

Fraction Calculator

Posted: Sun Mar 29, 2009 7:26 pm
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;
	}
}

Re: Fraction Calculator

Posted: Sun Mar 29, 2009 7:37 pm
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!

Re: Fraction Calculator

Posted: Sun Mar 29, 2009 8:54 pm
by Maevik
Thanks!
I've been trying to practice good commenting, so hopefully it's easy to read.

Re: Fraction Calculator

Posted: Tue Mar 31, 2009 9:53 pm
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

Re: Fraction Calculator

Posted: Wed Apr 01, 2009 2:23 am
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?

Re: Fraction Calculator

Posted: Wed Apr 01, 2009 8:45 pm
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.