NSNumber сравнить: возвращая разные результаты - PullRequest
7 голосов
/ 10 февраля 2012

Я пытаюсь провести сравнение чисел и получаю странные результаты.

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

Выход журнала:

NO
ДА
NO
ДА

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

Ответы [ 4 ]

7 голосов
/ 10 февраля 2012

Я попытался использовать isEqualToNumber:, и он вернул НЕТ.Причина, по которой они не совпадают, заключается в том, что 1.004 нельзя представить точно в двоичном виде.Приближение double имеет больше цифр после десятичной точки, чем приближение float, поэтому два числа различны.Обычно при сравнении чисел с плавающей запятой вы проверяете, равны ли они в пределах допустимого значения.

2 голосов
/ 10 февраля 2012

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

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

этот вопрос о переполнении стека также дает подробный ответ

2 голосов
/ 10 февраля 2012

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

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

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

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

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

0 голосов
/ 09 ноября 2018

Другое решение, которое работало для меня, состояло в том, чтобы преобразовать их в NSStrings и сделать сравнение строк.Это потому, что я работаю с 4-значными десятичными ценами, и это хорошо сработало для меня.

-(BOOL) hasPriceChanged{
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    formatter.numberStyle = NSNumberFormatterDecimalStyle;
    formatter.maximumFractionDigits = 4;
    NSString *p = [formatter stringFromNumber:self.price];
    NSString *op = [formatter stringFromNumber:self.originalPrice];
    return ![p isEqualToString:op];
}

Это избавило меня от проблемы, когда я сравнивал двойное число с двумя конечными нулями и число с плавающей запятой без него.1004 *

Я бы обычно предупреждал против сравнения строк, как это, но так как мои правила всегда были исправлены, для меня это было нормально в этом случае.

...