2012-02-09 3 views
7

Я пытаюсь выполнить некоторые сравнения чисел, и у меня возникают некоторые странные результаты.NSNumber compare: возвращает разные результаты

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"); 

Выход Вход:

НЕТ
ДА
НЕТ
ДА

Это очень расстраивает меня. Я знаю, это, вероятно, потому, что разница между количеством бит в поплавке по сравнению с двойным. Мне кажется, что он обрезает двойную до float, чтобы сравнить. Но если я не знаю, как создается число, как мне получить правильные результаты? Есть ли другой способ сравнить NSNumber?

+3

Что если вы используете '[number1 isEqualToNumber: number2] ', это предпочтительный способ, по крайней мере, если вы только пытаетесь установить равенство ценностей? – twilson

+1

Извините twilson, который все еще возвращает NO. Цифры различны. – picciano

ответ

7

Я попытался с помощью isEqualToNumber: и он вернулся NO. Причина, по которой они не совпадают, состоит в том, что 1.004 невозможно представить точно в двоичном формате. Аппроксимация double имеет больше цифр после десятичной точки, чем приближение float, поэтому два числа отличаются друг от друга. Как правило, при сравнении чисел с плавающей точкой, то тест, чтобы увидеть, если они равны в пределах значения допуска 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"); 

результаты:

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 
+0

Очень хорошее решение! Мне кажется безумным, что Apple не справляется с этим методом сравнения: –

+5

Apple (или, точнее, писатели компилятора) не может делать предположений о терпимости программиста к равенству. Все, что они могут сделать, это именно то, что сказал им программист. В этом случае вы сравнивали два неравных значения.Числа с плавающей точкой не являются тривиальным предметом. Если у вас есть время, взгляните на [Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой) (http://www-users.math.umd.edu/~jkolesar/mait613/floating_point_math.pdf). – SSteve

+0

Спасибо за ссылку! Я искал рекомендованное для этого чтение. Я знаю об этой проблеме какое-то время, просто ничего не сделал. :) –

2

Метод сравнения: соответствует стандартным правилам C для преобразования типов. Например, если вы сравниваете объект NSNumber, который имеет целочисленное значение с объектом NSNumber, имеющим значение с плавающей запятой, целочисленное значение преобразуется в значение с плавающей запятой для сравнения.

Кроме того, инициализация NSNumber с поплавком, а затем преобразование его в двойной теряет некоторую точность.

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 

Если вы знаете, может иметь соединение поплавка и удваивается, одно решение сравнить floatValues ​​NSNumber, как вы делали в последней строке фрагмента кода в вашем вопросе.

Кроме того, вы можете получить тип данных в NSNumber с помощью метода objCType.

+0

Мне очень нравится этот objType! Спасибо! –

2

пожалуйста, попробуйте isEqualToNumber: метод, чтобы проверить ваше состояние

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

this stackoverflow question также дает подробный ответ

+0

Это не будет работать так, как ожидалось, и будет записывать «NO». NSNumbers, инициализированные с помощью float и double, будут разными значениями. См. Мой ответ выше. Вопрос SO, на который вы ссылаетесь, использует все поплавки. – picciano

Смежные вопросы