Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

iMasterWeb

macrumors regular
Original poster
Mar 15, 2009
158
0
Hi there! So I'm learning Objective-C with the book: Programming in Objective-C 2.0 by Stephen Kochan. One of the programs use in the book is a program for adding fractions. Well, I'm the kind of guy that likes to take things to the next level, so I edited the program so that it also subtracts, and makes all improper fraction mixed numbers. One of the problems I'm having is that when doing subtraction I always get the same answer. Here's my code. (I'll add comments so you can keep track of what's going on)

The @interface file:
Code:
//you shouldn't need much explanation here
#import <Foundation/Foundation.h>

@interface Fraction: NSObject {
	int numerator;
	int denominator;
}

@property int numerator, denominator;
-(void)    print;
-(void)    setTo: (int) n over: (int) d;
-(double)  convertToNum;
-(void)    add: (Fraction *) f;
-(void)    simplify;
-(void)    getInput;
-(void)    subtract: (Fraction *) f;

@end

The @implementation file:
Code:
#import "Fraction.h"


@implementation Fraction

@synthesize numerator, denominator;


-(void) print  //print the fraction
{
	NSLog(@"%i/%i", numerator, denominator);
}
-(double) convertToNum //not sure what this does, but he used it in the book
{
	if(denominator != 0)
		return (double) numerator/denominator;
	else
		return 1.0;
}
-(void) setTo: (int) n over: (int) d //manually set the value of the numerator and denominator 
{
	numerator = n;
	denominator = d;
}
-(void) add: (Fraction *) f //add 2 fractions
{
//algorithm (it is correct)
	numerator = numerator * f.denominator + denominator * f.numerator;
	denominator = denominator * f.denominator;
	[self simplify];
}
-(void) simplify //simplifies the fractions
{
	int d = 0;
	int e = numerator;
	double wholeNumber;
	int n = denominator;
	
                while (n != 1) {
                //simplifies fractions
		if(denominator != 1 && denominator % n == 0 && numerator % n == 0) {
			denominator = denominator / n;
			numerator = numerator / n;
		} else --n;
	}
        //identifies improper fractions and makes them mixed numbers
	if(numerator > denominator) {
		NSLog(@"Improper fraction");
		numerator = numerator % denominator;
		do{
			e = numerator - denominator;
			++wholeNumber;
		} while(e - denominator > denominator);
		do{
			++d;
		}
		while(d < wholeNumber);
		wholeNumber = d;
		
		NSLog(@"%f %i/%i", wholeNumber, numerator, denominator);
	} else NSLog(@"%i/%i", numerator, denominator);
}


-(void) getInput
{
        //gets fraction input from user
	int usrNum, usrDen;
	scanf("%i/%i", &usrNum, &usrDen);
	numerator = usrNum;
	denominator = usrDen;
}
-(void) subtract: (Fraction *) f //subtracts fraction PROBLEM HERE!
{
		//makes fractions have same denominator
                if(denominator != f.denominator) {
		denominator = denominator * f.denominator;
		f.denominator = f.denominator * denominator;
		numerator = numerator * denominator;
		f.numerator = f.numerator * f.denominator;
	}
//checks if solution will be a negative (still working on this this is what I'm, trying to figure out!!)
	if(numerator / denominator < f.numerator / f.denominator) {
		numerator = f.numerator - numerator;
		denominator = denominator;
	} else {
	numerator = numerator - f.numerator;
	denominator = denominator;
	}
		[self simplify];

}
@end

The main file
Code:
#import "Fraction.h"


int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	Fraction *myFraction = [[Fraction alloc] init];
	Fraction *myFraction2 = [[Fraction alloc] init];
	char operator;
	NSLog(@"Would you like to add or substract? (enter '-' for subtraction and '+' for addition)");
	scanf("%c", &operator);
	//checks whether user wants to add or subtract
        switch (operator) {
		case '+':
			//adds
                        NSLog(@"Please print your first fraction in the format num/den.");
			[myFraction getInput];
			NSLog(@"Please print your second fraction in the format num/den.");
			[myFraction2 getInput];
			[myFraction print];
			NSLog(@"+");
			[myFraction2 print];
			NSLog(@"=");
			[myFraction add: myFraction2];
			break;
		case '-':
			//subtracts
                        NSLog(@"Please print your first fraction in the format num/den.");
			[myFraction getInput];
			NSLog(@"Please print your second fraction in the format num/den.");
			[myFraction2 getInput];
			[myFraction print];
			NSLog(@"-");
			[myFraction2 print];
			NSLog(@"=");
			[myFraction subtract: myFraction2];
			break;
	}
	[myFraction release];
	[myFraction2 release];
	
    [pool drain];
    return 0;
}

I know that's a TON so if ANYONE can offer words of advice, or a basic way to clean up the coding, please help! Thanks!!!

-iMaster
 
Code:
-(void) subtract: (Fraction *) f //subtracts fraction PROBLEM HERE!
{
		//makes fractions have same denominator
                if(denominator != f.denominator) {
		denominator = denominator * f.denominator;
		f.denominator = f.denominator * denominator;
		numerator = numerator * denominator;
		f.numerator = f.numerator * f.denominator;
	}
//checks if solution will be a negative (still working on this this is what I'm, trying to figure out!!)
	if(numerator / denominator < f.numerator / f.denominator) {
		numerator = f.numerator - numerator;
		denominator = denominator;
	} else {
	numerator = numerator - f.numerator;
	denominator = denominator;
	}
		[self simplify];

}

So this is the code we care about...

There's quite a bit going on here:
Code:
if(denominator != f.denominator) {
		denominator = denominator * f.denominator;
		f.denominator = f.denominator * denominator;
		numerator = numerator * denominator;
		f.numerator = f.numerator * f.denominator;
}
So if the denominators are different, you want to get a common denominator. Your strategy is to simply multiply each fraction by the others denominator over itself. So it SHOULD look something like:
Code:
if(denominator != f.denominator) {
  numerator *= f.denominator;
  f.numerator *= denominator;
  denominator *= f.denominator;
  f.denominator = denominator;
}

If you calculate a new denominator first, then use it to multiply by a numerator, you aren't going to get the correct result. Note that this is changing f that is passed in. if desired, you could set up a new Fraction to use when doing this, or call simplify on f after your calculation, though this seems a bit wasteful. You could also just set up a temporary numerator, as it is all you will care about for your calculation. if the denominators are equal, you can just assign f.numerator to this temporary variable, otherwise you can assign f.numerator*denominator to this value. You will still alter your members numerator and denominator after this. That would look something like:
Code:
int tempNumerator = 0;
if(f.denominator == denominator) {
  tempNumerator = f.numerator;
} else {
  tempNumerator = f.numerator * denominator;
  numerator *= f.denominator;
  denominator *= f.denominator;
}

Next, there's this:
Code:
	if(numerator / denominator < f.numerator / f.denominator) {
		numerator = f.numerator - numerator;
		denominator = denominator;
	} else {
		numerator = numerator - f.numerator;
		denominator = denominator;
	}

There's no compelling reason, as far as i can tell, to do this. Instead:
Code:
numerator -= f.numerator;
or
Code:
numerator -= tempNumerator;
if you used the temp as described above.

This might result in a negative numerator. That is OK.

Since numerator and denominator are integers, when you are doing division as you did in your condition, this is integer division. This is essentially division with no remainder, so the result will only be the whole portion of the result. With proper fractions, this will always yield 0.

I didn't review the rest of the code, but see if this gets you started.

-Lee
 
IDK if this helps, but here's what happens in the Console:

Code:
[Session started at 2009-08-31 22:23:22 -0500.]
2009-08-31 22:23:22.482 FractionTest[9394:903] Would you like to add or substract? (enter '-' for subtraction and '+' for addition)
-
2009-08-31 22:23:25.451 FractionTest[9394:903] Please print your first fraction in the format num/den.
7/8
2009-08-31 22:23:32.132 FractionTest[9394:903] Please print your second fraction in the format num/den.
23/24
2009-08-31 22:23:35.003 FractionTest[9394:903] 7/8
2009-08-31 22:23:35.004 FractionTest[9394:903] -
2009-08-31 22:23:35.004 FractionTest[9394:903] 23/24
2009-08-31 22:23:35.005 FractionTest[9394:903] =
2009-08-31 22:23:35.005 FractionTest[9394:903] Improper fraction
2009-08-31 22:23:35.006 FractionTest[9394:903] 1.000000 0/1

The Debugger has exited with status 0.

I ALWAYS get 1.000000 0/1 when I do subtraction that results in a negative number, this is what I'm trying to fix.

Thanks!!

-iMaster
 
Code:
-(void) simplify //simplifies the fractions
{
	int d = 0;
	int e = numerator;
	double wholeNumber;
	int n = denominator;
	
                while (n != 1) {
                //simplifies fractions
		if(denominator != 1 && denominator % n == 0 && numerator % n == 0) {
			denominator = denominator / n;
			numerator = numerator / n;
		} else --n;
	}
        //identifies improper fractions and makes them mixed numbers
	if(numerator > denominator) {
		NSLog(@"Improper fraction");
		numerator = numerator % denominator;
		do{
			e = numerator - denominator;
			++wholeNumber;
		} while(e - denominator > denominator);
		do{
			++d;
		}
		while(d < wholeNumber);
		wholeNumber = d;
		
		NSLog(@"%f %i/%i", wholeNumber, numerator, denominator);
	} else NSLog(@"%i/%i", numerator, denominator);
}

So now to simplify:
One: Why is wholeNumber a double and not an int? What is all this jazz with using d to get an int, instead of using an int in the first place.
Two: At this point, it may be easier to get a temporary value that is the absolute value of your numerator, and track if the original numerator is less than 0. After the very beginning when you find the GCD and divide through, you don't want to modify numerator or denominator, as you have done the simplification. Using a temp variable with the absolute value of the numerator instead should let you keep your current algorithm in tact without modifying numerator, etc.. At the end, you'd just print - if numerator is < 0, and nothing otherwise.

-Lee
 
I can answer your first question Lee, wholeNumber is a double because when it was an int, it always came out to a really long, random negative integer, but when it was a double it seemed to fix this. Even so, I still sometimes got a decimal with a double so d fixes this. It goes up in increments of 1 until it is greater than wholeNumber, and then it transfers that value to wholeNumber.
As for your second question, could you run that by me again? :eek: I'm a little confused, lol, sorry noob here.

Hope that makes sense, thanks!!

-iMaster
 
I'm just going to make some minor tweaks to your code, but ultimately it would be of value to have a more efficient GCD algorithm available.

Code:
-(void) simplify //simplifies the fractions
{
        int wholeNumber = 0;
        int n = abs(denominator) > abs(numerator) ? abs(numerator) : abs(denominator);

        //Replace this with Euclid's Algorithm
        while (n != 1) {
                //simplifies fractions
                if(denominator != 1 && denominator % n == 0 && numerator % n == 0) {
                        denominator = denominator / n;
                        numerator = numerator / n;
                        break;
                } else --n;
        }
        //identifies improper fractions and makes them mixed numbers
        int tmpNum = abs(numerator);
        int mult = tmpNum != numerator ? -1 : 1;
        if(tmpNum > denominator) {
                NSLog(@"Improper fraction");
                tmpNum = tmpNum % denominator;
                int e = 0;
                do{
                        e = tmpNum - denominator;
                        ++wholeNumber;
                } while(e - denominator > denominator);
                wholeNumber *= mult;
                NSLog(@"%i %i/%i", wholeNumber, tmpNum, denominator);
        } else NSLog(@"%i/%i", numerator, denominator);
}

I switched this over to C because I'm not at a Mac, and just subbed back in NSLog for printf. I tried this for a number of things, including negatives, etc. and it seems to do what you want. I left your algorithms intact, but it's fairly easy to just do:
wholeNumber = numerator / denominator;
tmpNum = numerator % denominator;
instead of the loop with substraction. As for the GCD... again, C implementations of Euclid's Algorithm are readily available if you google around, and it would be far more efficient.

-Lee
 
As for the GCD... again, C implementations of Euclid's Algorithm are readily available if you google around, and it would be far more efficient.

-Lee

Thanks Lee for all the help here. There is a reduce method for the Fraction class shown in the book that uses the Euclidean algorithm to find the GCD. I'm not sure why it's not being used here.

Cheers,

Steve Kochan
 
Here is the new simplify method, with your changes and Euclid's algorithim (the reason I didn't use it in the first place is because I wanted to see if I could make a simplify method myself :eek:)

Code:
-(void) simplify
{
	int wholeNumber = 0;
	int n = abs(denominator) > abs(numerator) ? abs(numerator) : abs(denominator);
	int u = numerator;
	int v = denominator;
	int temp;
	while (v != 0) {
		temp = u % v;
		u = v;
		v = temp;
	}
	numerator /= u;
	denominator /= u;
	int tmpNum = abs(numerator);
	int mult = tmpNum != numerator ? -1 : 1;
	if(tmpNum > denominator) {
		NSLog(@"Improper fraction");
		tmpNum = tmpNum % denominator;
		int e = 0;
		do{
			e = tmpNum - denominator;
			++wholeNumber;
		} while(e - denominator > denominator);
		wholeNumber *= mult;
		NSLog(@"%i %i/%i", wholeNumber, tmpNum, denominator);
	} else NSLog(@"%i/%i", numerator, denominator);
}

And the subtract method:
Code:
-(void) subtract: (Fraction *) f
{
		if(denominator != f.denominator) {
		denominator *= f.denominator;
		f.denominator *= denominator;
		numerator *= denominator;
		f.numerator *= f.denominator;
	}
	if(numerator / denominator < f.numerator / f.denominator) {
		numerator = f.numerator - numerator;
		denominator = denominator;
	} else {
	numerator -= f.numerator;
	}
		[self simplify];

}

Now subtraction is not working when the two fractions don't have the same denominator, this is what I get in the console, when trying to subtract 5/6 and 1/3 (which should equal 1/2 when simplified)
Code:
2009-09-01 10:23:44.269 FractionTest[10994:903] Would you like to add or substract? (enter '-' for subtraction and '+' for addition)
-
2009-09-01 10:23:46.533 FractionTest[10994:903] Please print your first fraction in the format num/den.
5/6
2009-09-01 10:23:49.557 FractionTest[10994:903] Please print your second fraction in the format num/den.
1/3
2009-09-01 10:23:51.901 FractionTest[10994:903] 5/6
2009-09-01 10:23:51.902 FractionTest[10994:903] -
2009-09-01 10:23:51.903 FractionTest[10994:903] 1/3
2009-09-01 10:23:51.903 FractionTest[10994:903] =
2009-09-01 10:23:51.905 FractionTest[10994:903] Improper fraction
2009-09-01 10:23:51.906 FractionTest[10994:903] 1 0/1

Thanks for all of the help!!!

-iMaster
 
Also could you explain some of the things going on here, for example:
Code:
int n = abs(denominator) > abs(numerator) ? abs(numerator) : abs(denominator);

You are using the conditional operator here, but what is the "abs" for?

Also, I'm not sure where to put "n" in my code? should it replace a variable? ("u" or "v" possibly?)

Thanks!

-iMaster
 
I posted this above:
Code:
if(denominator != f.denominator) {
  numerator *= f.denominator;
  f.numerator *= denominator;
  denominator *= f.denominator;
  f.denominator = denominator;
}

Compare this to your new version:
Code:
	if(denominator != f.denominator) {
		denominator *= f.denominator;
		f.denominator *= denominator;
		numerator *= denominator;
		f.numerator *= f.denominator;
	}

The order is very important here. If you change the denominator(s) first, bad things are going to happen. Note, though, that either of these is changing the fraction that is passed in. I consider this a bad thing. I suggested this:
Code:
int tempNumerator = 0;
if(f.denominator == denominator) {
  tempNumerator = f.numerator;
} else {
  tempNumerator = f.numerator * denominator;
  numerator *= f.denominator;
  denominator *= f.denominator;
}
instead. tempNumerator would be used as the value passed in.

Also, you still have the code that doesn't allow for a negative numerator, and i don't understand why.

abs takes the absolute value of an int. I did the calculation of n to accommodate for the algorithm you used to find the GCD. You wouldn't very well want to end up with a negative value, and subtract forever (or until an underflow and wrap-around). If you're using Euclid's, you shouldn't need it.

-Lee
 
Ok, I was thinking through the multiplication incorrectly, my mistake. Fixed now.

It seems to be working now (except negatives, obviously), so what part am I disallowing negative numbers in? Is it this part?
Code:
	int tempNumerator = 0;
	if(f.denominator == denominator) {
		tempNumerator = f.numerator;
	} else {
		tempNumerator = f.numerator * denominator;
		numerator *= f.denominator;
		denominator *= f.denominator;
	}
	if(numerator / denominator < f.numerator / f.denominator) {
		numerator = f.numerator - numerator;
		denominator = denominator;
	} else {
	numerator -= tempNumerator;

I changed
Code:
numerator -= tempNumerator;
from
Code:
numerator -= f.numerator;

I know I sound like a complete idiot right now, but I really do appreciate the help! :D:D:D

-iMaster
 
Just do:
numerator -= tempNumerator;
unconditionally.

Your condition is using integer division anyway, so you're not likely to get what you expect from it.

If it ends up negative, so be it. If you want to just get the difference and not actually subtract, you could do it how you have it set up now, but you'd just need to compare numerator to tempNumerator at that point, not do division in your condition.

-Lee
 
What do you mean "unconditionally"?

What do I need to change to make this work?
Code:
-(void) simplify
{
	int wholeNumber = 0;
//where should I put the following int in the program???	
int n = abs(denominator) > abs(numerator) ? abs(numerator) : abs(denominator);
	int u = numerator;
	int v = denominator;
	int temp;
	while (v != 0) {
		temp = u % v;
		u = v;
		v = temp;
	}
	numerator /= u;
	denominator /= u;
	int tmpNum = abs(numerator);
	int mult = tmpNum != numerator ? -1 : 1;
	if(tmpNum > denominator) {
		NSLog(@"Improper fraction");
		tmpNum = tmpNum % denominator;
		int e = 0;
		do{
			e = tmpNum - denominator;
			++wholeNumber;
		} while(e - denominator > denominator);
		wholeNumber *= mult;
		NSLog(@"%i %i/%i", wholeNumber, tmpNum, denominator);
	} else NSLog(@"%i/%i", numerator, denominator);
}



-(void) subtract: (Fraction *) f
{
	int tempNumerator = 0;
	if(f.denominator == denominator) {
		tempNumerator = f.numerator;
	} else {
		tempNumerator = f.numerator * denominator;
		numerator *= f.denominator;
		denominator *= f.denominator;
		//I know you didn't have this line in your example, and I'll take it out if necessary, but f.denominator would also have to change, not just denominator (the denominators both need to be multiplied by each other)
                f.denominator *= denominator;
	}
//this part doesn't seem to be working..(this is how I'm getting it to identify problems with a negative solution, eventually I'll use a boolean so that if "negative" is true, then the solution will print with a negative sign in front of it)
	if(numerator / denominator < f.numerator / f.denominator) {
		numerator = f.numerator - numerator;
	}
// Is this what you meant by unconditionally? (take it out of the loop)
	numerator -= tempNumerator;
	[self simplify];
}

THANK YOU SO MUCH! I know how annoying I'm being right now :D:D

-iMaster
 
Here's what I came up with:
Code:
-(void) simplify
{
	int wholeNumber = 0;
	int u = numerator;
	int v = denominator;
	int temp;
	while (v != 0) {
		temp = u % v;
		u = v;
		v = temp;
	}
	u = abs(u);
	numerator /= u;
	denominator /= u;

	int tmpNum = abs(numerator);
	int mult = tmpNum != numerator ? -1 : 1;
	if(tmpNum > denominator) {
		NSLog(@"Improper fraction");
		wholeNumber = tmpNum / denominator;
		tmpNum = tmpNum % denominator;
		wholeNumber *= mult;
		NSLog(@"%i %i/%i", wholeNumber, tmpNum, denominator);
	} else NSLog(@"%i/%i", numerator, denominator);
}



-(void) subtract: (Fraction *) f
{
	int tempNumerator = 0;
	if(f.denominator == denominator) {
		tempNumerator = f.numerator;
	} else {
		tempNumerator = f.numerator * denominator;
		numerator *= f.denominator;
		denominator *= f.denominator;
	}
	numerator -= tempNumerator;
	[self simplify];
}

The changes were:
n is no longer needed, as noted above.

Don't change f. When you're subtracting one fraction from the other, there's no reason to modify the right hand operand. You know what the common denominator is, you don't need to put that in f.denominator, you just need to know what the numerator of f would be if you did make this change.

Unconditionally just means without a condition, so no if needed.

An abs was thrown into simplify, so you'd end up with a negative numerator instead of a negative denominator.

The conversion to a whole number and fractional part was changed as i mentioned above, to use / and % instead of a series of subtractions.

There may have been a few other changes, but that's what i recall changing.

-Lee
 
Thank you sooooooo much! You've been a HUGE help! Its working perfectly now!!!
 
No problem. Now change getInput and setTo:n over:d to handle signs properly... i.e. if -2/-3 is entered, change this to 2/3... if 2/-3 move the sign to the numerator. Basically, ensure that the denominator is always positive, and the numerator carries the sign.

-Lee
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.