Different ways of comparing NSDecimalNumber

X Slash picture X Slash · Jan 27, 2012 · Viewed 17.1k times · Source

For example, with primitive, I'll do this

if ( (x >= 6000) && (x <= 20000) )
    // do something here 

and with NSDecimalNumber, this is what I have

if ( (([x compare:[NSNumber numberWithInt:6000]] == NSOrderedSame) || 
        ([x compare:[NSNumber numberWithInt:6000]] == NSOrderedDescending))
    && (([x compare:[NSNumber numberWithInt:20000]] == NSOrderedSame) || 
        ([x compare:[NSNumber numberWithInt:6000]] == NSOrderedAscending)) )
{
    // do something here
}

Is there any other ways (easier and more elegant) to this comparison? If I convert the value to primitive, what primitive do I use? I don't want to use CGFloat, float or double, as I'm handling with currency here. Or if I do convert them to those mentioned above, can someone verify / explain about the precision?

Answer

theTRON picture theTRON · Jan 27, 2012

My understanding is that you can only compare NSDecimalNumber and NSNumber objects using the compare: method. Super frustrating, but I believe it stems from Objective-C not supporting operator overloading.

If it's becoming really difficult to read, you could always add a category with some helper methods to try and make it a little more readable, something like this perhaps?

// NSNumber+PrimativeComparison.m

- (NSComparisonResult) compareWithInt:(int)i{
    return [self compare:[NSNumber numberWithInt:i]]
}

- (BOOL) isEqualToInt:(int)i{
    return [self compareWithInt:i] == NSOrderedSame;
}

- (BOOL) isGreaterThanInt:(int)i{
    return [self compareWithInt:i] == NSOrderedDescending;
}

- (BOOL) isGreaterThanOrEqualToInt:(int)i{
    return [self isGreaterThanInt:i] || [self isEqualToInt:i];
}

- (BOOL) isLessThanInt:(int)i{
    return [self compareWithInt:i] == NSOrderedAscending;
}

- (BOOL) isLessThanOrEqualToInt:(int)i{
    return [self isLessThanInt:i] || [self isEqualToInt:i];
}

Then things become a little more human-readable:

if([x isGreaterThanOrEqualToInt:6000] && [x isLessThanOrEqualToInt:20000]){
    //...
}

Edit I just noticed that you'd also asked about why using NSDecimalNumber is optimal in currency scenarios. This answer gives a pretty good run down on why floats (and doubles) are not precise enough when working with currency. Furthermore, Apple's documentation for NSDecimalNumber recommends its use whenever you're doing base-10 arithmetic.