2015-05-14 3 views
0

Да, я прочитал другие сообщения о stackoverflow о сравнении NSNumber, и ни один из них, похоже, не справляется с этой конкретной ситуацией.
Это решение было особенно плохо ... NSNumber compare: returning different results
Поскольку предлагаемое решение не работает вообще. Использование абс (значение1 - значение2) < Допуск устранен с самого начала, поскольку дробные значения удаляются, что делает допущение несущественным.Сравнение экземпляров NSNumber с isEqualToNumber

И из документации Apple ... NSNumber явно не гарантирует, что возвращаемый тип будет соответствовать методу, используемому для его создания. Другими словами, если вам присвоен NSNumber, у вас нет способа определить, содержит ли он float, double, int, bool или что-то еще.

Кроме того, насколько я могу судить, NSNumber isEqualToNumber является ненадежным методом сравнения двух NSNumbers.

Поэтому, учитывая эти определения ...

NSNumber *float1 = [NSNumber numberWithFloat:1.00001]; 
NSNumber *double1 = [NSNumber numberWithDouble:1.00001]; 

Если вы запустите отладчик, а затем сделать 2 сравнение этих одинаковых чисел с помощью ==, один выходит из строя, а другие нет.

p [double1 floatValue] == [float1 floatValue] **// returns true** 
p [double1 doubleValue] == [float1 doubleValue] **// returns false** 

Если сравнить их с помощью isEqualToNumber

p [float1 isEqualToNumber:double1]    **// returns false** 

Так что если isEqualToNumber собирается вернуться ложным, учитывая, что создание NSNumber черный ящик, который может дать вам какой-либо другой тип на пути я не уверен, какой хороший способ.

Итак, если вы собираетесь провести проверку на наличие загрязнений, потому что существующее значение было изменено на новое значение ... какой самый простой способ сделать это, чтобы обрабатывать все сопоставления NSNumber .. не просто плавать и двойной, но все NSNumbers?

Кажется, что преобразование в строковое значение, тогда сравнение было бы наиболее полезным или, возможно, большим количеством дополнительного кода с использованием NSNumberFormatter.

Что вы думаете?

+0

Вашего 'float3' не включает в себя определение. Возможно, вы имели в виду 'float1'? –

+0

Ой, да, я исправлю. –

+2

@ user3055655 Вам, вероятно, просто нужно использовать 'fabs' или' fabsf' для проверки допуска, а не 'abs' –

ответ

4

Невозможно надежно сравнить два поплавка или двойные поплавки IEEE. Это не имеет никакого отношения к NSNumber. Это характер плавающей запятой. Это обсуждается в контексте простых типов C при Strange problem comparing floats in objective-C. Только правильный способ сравнения чисел с плавающей запятой - это тестирование против допуска. Я не знаю, что вы подразумеваете под «дробными значениями». Некоторые цифры всегда потеряны в представлении с плавающей запятой.

Особое тестовое значение, которое вы предоставили, демонстрирует проблемы довольно хорошо. 1.00001 не может быть точно выражен в конечном числе двоичных цифр. Wolfram Alpha - отличный способ изучить это, но в виде двойного, 1.00001 раундов до 1.0000100000000001. Как плавающий, он округляется до 1.00001001. Очевидно, эти числа не равны. Если вы обойдете их по-разному, вам не следует удивлять, что isEqualToNumber: не работает. Это должно четко указать, почему ваши два звонка floatValue оказываются равными. Закругленные до точности поплавка, они «достаточно близки».

Если вы хотите сравнить числа с плавающей запятой, вы должны сравнить их с эпсилон.Учитывая последние достижения в оптимизации компилятора, даже два идентичных фрагментов кода C с плавающей запятой могут генерировать несколько разные значения в их младших значащих разрядах, если вы используете -Ofast (мы получаем большие преимущества в производительности, позволяя это).

Если вам нужно определенное количество значительных дробных цифр, то обычно лучше работать в нотации с фиксированной точкой. Просто масштабируйте все по количеству цифр, которые вам нужны, и работайте целыми числами. Если вам нужна плавающая точка, но просто хотите, чтобы base-10 работал хорошо (а не base-2), используйте NSDecimalNumber или NSDecimal. Это переместит ваши проблемы на то, что плохо происходит в базе-10. Но если вы работаете с плавающей точкой, вы должны иметь дело с ошибками округления.

Для гораздо более обширного обсуждения см "What Every Programmer Should Know About Floating-Point Arithmetic."

+0

Мне нравится «масштабировать все и работать с целыми числами». Интуитивно кажется, что это сработает, поэтому я пойду по этой дороге. То, что я имел в виду, чтобы удалить значения фракций, является абс функцией (абсолютное) значение собирается отменить .246 от 18.246, в результате чего вы сравниваете целые числа, а не определенное количество значащих цифр. Теперь, если вы добавите дополнительный шаг умножения на 100, например, THEN, сравнивая ... да, я думаю, что это сработает. –

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