NSNumber Comparison: Returning Different Results

I am trying to do some number comparisons and I am 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
NOT
YES

This is very unpleasant for me. I know this is probably because the difference between the number of bits in the float is compared to double. It seems to me that he cuts the double to swim to compare. But if I don’t know how a number is created, how can I get the right results? Is there any other way to compare NSNumber?

+7
source share
3 answers

I tried using isEqualToNumber: and it returned NO. The reason they do not match is because 1.004 cannot be represented exactly in binary format. The double approximation has more digits after the decimal point than the float approximation, so the two numbers are different. Usually, comparing floating point numbers, you check to see if they match the allowed 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 
+7
source

The compare method: follows 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.

In addition, by initializing an NSNumber with a float, converting it to 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 that you can have a combination of float and double, one solution is to compare NSNumber floatValues, as you did in the last line of your code snippet in your question.

Alternatively, you can get the data type in NSNumber using the objCType method.

+2
source

try isEqualToNumber : method for checking your status

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

this stack thread question also gives a detailed answer

+2
source

All Articles