views:

395

answers:

1

I've been coding my way through Steve Kochan's Programming in Objective-C 2.0 book. I'm up to an exercise in chapter 7, ex 4, in case anyone has the book.

The question posed by the exercise it will the Fraction class written work with negative fractions such as -1/2 + -2/3?

Here's the implementation code in question -

@implementation Fraction

@synthesize numerator, denominator;

-(void) print
{
NSLog(@"%i/%i", numerator, denominator);
}

-(void) setTo: (int) n over: (int) d
{
numerator = n;
denominator = d;
}

-(double) convertToNum
{
if (denominator != 0)
    return (double) numerator / denominator;
else
    return 1.0;
}

-(Fraction *) add: (Fraction *) f
{
// To add two fractions:
// a/b + c/d = ((a * d) + (b * c)) / (b * d)

// result will store the result of the addition
Fraction *result = [[Fraction alloc] init];
int resultNum, resultDenom;

resultNum = (numerator * f.denominator) + (denominator * f.numerator);
resultDenom = denominator * f.denominator;

[result setTo: resultNum over: resultDenom];
[result reduce];

return result;
}

-(Fraction *) subtract: (Fraction *) f
{
// To subtract two fractions:
// a/b - c/d = ((a * d) - (b * c)) / (b * d)

// result will store the result of the addition
Fraction *result = [[Fraction alloc] init];
int resultNum, resultDenom;

resultNum = numerator * f.denominator - denominator * f.numerator;
resultDenom = denominator * f.denominator;

[result setTo: resultNum over: resultDenom];
[result reduce];

return result;
}

-(Fraction *) multiply: (Fraction *) f
{
// To multiply two fractions
// a/b * c/d = (a*c) / (b*d)

// result will store the result of the addition
Fraction *result = [[Fraction alloc] init];
int resultNum, resultDenom;

resultNum = numerator * f.numerator;
resultDenom = denominator * f.denominator;

[result setTo: resultNum over: resultDenom];
[result reduce];

return result;
}

-(Fraction *) divide: (Fraction *) f
{
// To divide two fractions
// a/b / c/d = (a*d) / (b*c)

// result will store the result of the addition
Fraction *result = [[Fraction alloc] init];
int resultNum, resultDenom;

resultNum = numerator * f.denominator;
resultDenom = denominator * f.numerator;

[result setTo: resultNum over: resultDenom];
[result reduce];

return result;
}

-(void) reduce
{
int u = numerator;
int v = denominator;
int temp;

while (v != 0) {
    temp = u % v;
    u = v;
    v = temp;
}

numerator /= u;
denominator /= u;
}

@end

My question to you is will it work with negative fractions and can you explain how you know? Part of the issue is I don't know how to calculate negative fractions myself so I'm not too sure how to know.

Many thanks.

+2  A: 

The comments in the code give the formulas. Since you don't know them, it's reasonable for you to trust the comments are correct.

From plain common sense, you can also infer that the behavior of negative fractions doesn't depend on their sign.

For example, if you know how negative numbers work in general (think temperature for example), there is no reason for fractions to behave differently (it's quite fine to say that a temperature is 160/3 for example). After all, a fraction is but a number.

Now from my quick look at the code, I don't see any case where this code fools with the sign of its data. It never takes the absolute value of anything for example. So my conclusion is that it should work with negative fractions just fine.

Now if you have the option to actually run the code, you can check that on a few test cases.

That being said, there is one thing that this code doesn't handle: mathematically, a negative fraction is one where either the numerator or the denominator is negative (but not both). The convention is to prefer to the minus sign on the numerator. That code will not respect or enforce that convention.

That doesn't make the code wrong, though. However, this places a trap for future expansion. For example, suppose you expand that class to also compare two fractions for equality. The following naive implementation would be wrong:

- (BOOL) isEqualTo:(Fraction*)f
{
  [self reduce];
  [f reduce];
  BOOL b = (self.numerator == f.numerator) && (self.denominator == f.denominator);
  return b;
}

Such an implementation would wrongly claim that (-2)/3 is not equal to 2/(-3).

I leave it to you to imagine a better implementation.

Jean-Denis Muys
FYI: edited question to change `self.reduce;` etc to `[self reduce];`. Using dot syntax to invoke a method that's not a getter or setter of a property goes against their intention and should never be used.
Dave DeLong
indeed. Good catch
Jean-Denis Muys
Thanks for the reply and helping me understand this.
Mark Reid