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?
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.
@interface NSDecimalNumber (Comparison)
- (BOOL)isLessThan:(NSDecimalNumber *)decimalNumber;
- (BOOL)isLessThanOrEqualTo:(NSDecimalNumber *)decimalNumber;
- (BOOL)isGreaterThan:(NSDecimalNumber *)decimalNumber;
- (BOOL)isGreaterThanOrEqualTo:(NSDecimalNumber *)decimalNumber;
- (BOOL)isEqualToDecimalNumber:(NSDecimalNumber *)decimalNumber;
@end
@implementation NSDecimalNumber (Comparison)
- (BOOL)isLessThan:(NSDecimalNumber *)decimalNumber
{
return [self compare:decimalNumber] == NSOrderedAscending;
}
- (BOOL)isLessThanOrEqualTo:(NSDecimalNumber *)decimalNumber
{
return [self compare:decimalNumber] != NSOrderedDescending;
}
- (BOOL)isGreaterThan:(NSDecimalNumber *)decimalNumber
{
return [self compare:decimalNumber] == NSOrderedDescending;
}
- (BOOL)isGreaterThanOrEqualTo:(NSDecimalNumber *)decimalNumber
{
return [self compare:decimalNumber] != NSOrderedAscending;
}
- (BOOL)isEqualToDecimalNumber:(NSDecimalNumber *)decimalNumber
{
return [self compare:decimalNumber] == NSOrderedSame;
}
@end
@interface NSDecimalNumber_Comparison_Tests : XCTestCase
@end
@implementation NSDecimalNumber_Comparison_Tests
#pragma mark - isLessThan: tests
- (void)test_isLessThan_whenGreaterThan_returnsNO
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6779"];
BOOL result = [decimalNumberA isLessThan:decimalNumberB];
XCTAssertFalse(result);
}
- (void)test_isLessThan_whenLessThan_returnsYES
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6781"];
BOOL result = [decimalNumberA isLessThan:decimalNumberB];
XCTAssertTrue(result);
}
- (void)test_isLessThan_whenEqualTo_returnsNO
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
BOOL result = [decimalNumberA isGreaterThan:decimalNumberB];
XCTAssertFalse(result);
}
#pragma mark - isLessThanOrEqualTo: tests
- (void)test_isLessThanOrEqualTo_whenGreaterThan_returnsNO
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6779"];
BOOL result = [decimalNumberA isLessThanOrEqualTo:decimalNumberB];
XCTAssertFalse(result);
}
- (void)test_isLessThanOrEqualTo_whenLessThan_returnsYES
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6781"];
BOOL result = [decimalNumberA isLessThanOrEqualTo:decimalNumberB];
XCTAssertTrue(result);
}
- (void)test_isLessThanOrEqualTo_whenEqualTo_returnsYES
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
BOOL result = [decimalNumberA isLessThanOrEqualTo:decimalNumberB];
XCTAssertTrue(result);
}
#pragma mark - isGreaterThan: tests
- (void)test_isGreaterThan_whenGreaterThan_returnsYES
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6779"];
BOOL result = [decimalNumberA isGreaterThan:decimalNumberB];
XCTAssertTrue(result);
}
- (void)test_isGreaterThan_whenLessThan_returnsNO
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6781"];
BOOL result = [decimalNumberA isGreaterThan:decimalNumberB];
XCTAssertFalse(result);
}
- (void)test_isGreaterThan_whenEqualTo_returnsNO
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
BOOL result = [decimalNumberA isGreaterThan:decimalNumberB];
XCTAssertFalse(result);
}
#pragma mark - isGreaterThanOrEqualTo: tests
- (void)test_isGreaterThanOrEqualTo_whenGreaterThan_returnsYES
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6779"];
BOOL result = [decimalNumberA isGreaterThanOrEqualTo:decimalNumberB];
XCTAssertTrue(result);
}
- (void)test_isGreaterThanOrEqualTo_whenLessThan_returnsNO
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6781"];
BOOL result = [decimalNumberA isGreaterThanOrEqualTo:decimalNumberB];
XCTAssertFalse(result);
}
- (void)test_isGreaterThanOrEqualTo_whenEqualTo_returnsYES
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
BOOL result = [decimalNumberA isGreaterThanOrEqualTo:decimalNumberB];
XCTAssertTrue(result);
}
#pragma mark - isEqualToDecimalNumber: tests
- (void)test_isEqualToDecimalNumber_whenGreaterThan_returnsNO
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6779"];
BOOL result = [decimalNumberA isEqualToDecimalNumber:decimalNumberB];
XCTAssertFalse(result);
}
- (void)test_isEqualToDecimalNumber_whenLessThan_returnsNO
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6781"];
BOOL result = [decimalNumberA isEqualToDecimalNumber:decimalNumberB];
XCTAssertFalse(result);
}
- (void)test_isEqualToDecimalNumber_whenEqualTo_returnsYES
{
NSDecimalNumber *decimalNumberA = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
NSDecimalNumber *decimalNumberB = [NSDecimalNumber decimalNumberWithString:@"4.6780"];
BOOL result = [decimalNumberA isEqualToDecimalNumber:decimalNumberB];
XCTAssertTrue(result);
}
@end
compare
method returns NSOrderedDescending
, NSOrderedAscending
or NSOrderedSame
Instead you can then easily write
if (
[x compare:[NSNumber numberWithInt:6000]] != NSOrderedAscending &&
[x compare:[NSNumber numberWithInt:20000]] != NSOrderedDescending
)
{
// do something here
}
Which is slighty better readable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With