Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSNumber compare: returning different results

I'm trying to do some number comparisons and I'm getting some weird results.

NSNumber* number1 = [NSNumber numberWithFloat:1.004];
NSNumber* number2 = [NSNumber numberWithDouble:1.004];

([number1 compare:number2] == NSOrderedSame) ? NSLog(@"YES") : NSLog(@"NO");
([number1 compare:number2] == NSOrderedAscending) ? NSLog(@"YES") : NSLog(@"NO");
([number1 doubleValue] == [number2 doubleValue]) ? NSLog(@"YES") : NSLog(@"NO");
([number1 floatValue] == [number2 floatValue]) ? NSLog(@"YES") : NSLog(@"NO");

Log output:

NO
YES
NO
YES

This is extremely frustrating to me. I know this is probably because the difference between the number of bits in a float compared to a double. It seems to me it's truncating the double down to a float to do the compare. But if I don't know how the number is created, how do I get the correct results? Is there a different way to compare NSNumber's?

like image 430
Matt W. Avatar asked Feb 09 '12 22:02

Matt W.


3 Answers

I tried using isEqualToNumber: and it returned NO. The reason they aren't the same is because 1.004 can't be represented exactly in binary. The double approximation has more digits after the decimal point than the float approximation so the two numbers are different. Normally, when comparing floating point numbers, you test to see if they are equal to within a tolerance value fabs(a - b) < tolerance:

NSNumber* number1 = [NSNumber numberWithFloat:1.004];
NSNumber* number2 = [NSNumber numberWithDouble:1.004];

NSLog(@"number 1: %.12f", [number1 doubleValue]);
NSLog(@"number 2: %.12f", [number2 doubleValue]);

([number1 isEqualToNumber:number2]) ? NSLog(@"YES") : NSLog(@"NO");
fabs([number1 floatValue] - [number2 doubleValue]) < 1.0e-7 ? NSLog(@"YES") : NSLog(@"NO");

results:

2012-02-09 15:08:34.272 so9219935[3313:903] number 1: 1.003999948502
2012-02-09 15:08:34.274 so9219935[3313:903] number 2: 1.004000000000
2012-02-09 15:08:34.275 so9219935[3313:903] NO
2012-02-09 15:08:34.275 so9219935[3313:903] YES
like image 140
SSteve Avatar answered Nov 12 '22 15:11

SSteve


please try the isEqualToNumber: method to check your condition

([number1 isEqualToNumber:number2]) ? NSLog(@"YES") : NSLog(@"NO");

this stackoverflow question also gives a detailed answer

like image 35
carbonr Avatar answered Nov 12 '22 16:11

carbonr


The compare: method follows the standard C rules for type conversion. For example, if you compare an NSNumber object that has an integer value with an NSNumber object that has a floating point value, the integer value is converted to a floating-point value for comparison.

Also, initializing an NSNumber with a float, then converting it to a double loses some precision.

NSNumber* number1 = [NSNumber numberWithFloat:1.004];
NSNumber* number2 = [NSNumber numberWithDouble:1.004];
NSLog(@"number1 double: %1.16f", [number1 doubleValue]);
NSLog(@"number2 double: %1.16f", [number2 doubleValue]);
NSLog(@"number1 objCType %s", [number1 objCType]);
NSLog(@"number2 objCType %s", [number2 objCType]);

2012-02-09 15:59:49.487 testNSNumber[89283:f803] number1 double: 1.0039999485015869
2012-02-09 15:59:49.488 testNSNumber[89283:f803] number2 double: 1.0040000000000000
2012-02-09 16:21:01.655 testNSNumber[4351:f803] number1 objCType f
2012-02-09 16:21:01.656 testNSNumber[4351:f803] number2 objCType d

If you know you may have a mix of float and doubles, one solution is to compare the NSNumber floatValues like you did in the last line of your code snippet in your question.

Also, you can get the type of the data in the NSNumber with the objCType method.

like image 2
picciano Avatar answered Nov 12 '22 14:11

picciano