简体   繁体   English

NSNumber比较:返回不同的结果

[英]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? 是否有不同的方法来比较NSNumber?

I tried using isEqualToNumber: and it returned NO. 我尝试使用isEqualToNumber:它返回NO。 The reason they aren't the same is because 1.004 can't be represented exactly in binary. 它们不相同的原因是因为1.004不能完全用二进制表示。 The double approximation has more digits after the decimal point than the float approximation so the two numbers are different. double float在小数点后的位数比float近似值更多,因此两个数字不同。 Normally, when comparing floating point numbers, you test to see if they are equal to within a tolerance value fabs(a - b) < tolerance : 通常,在比较浮点数时,测试它们是否等于容差值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

The compare: method follows the standard C rules for type conversion. compare:方法遵循标准C规则进行类型转换。 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. 例如,如果将具有整数值的NSNumber对象与具有浮点值的NSNumber对象进行比较,则将整数值转换为浮点值以进行比较。

Also, initializing an NSNumber with a float, then converting it to a double loses some precision. 此外,使用float初始化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

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. 如果你知道你可能有浮动和双打的混合,一种解决方案是比较你问题中代码片段最后一行中的NSNumber floatValues。

Also, you can get the type of the data in the NSNumber with the objCType method. 此外,您可以使用objCType方法获取NSNumber中的数据类型。

please try the isEqualToNumber : method to check your condition 请尝试使用isEqualToNumber :方法检查您的情况

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

this stackoverflow question also gives a detailed answer 这个stackoverflow问题也给出了详细的答案

Another solution that worked for me was to convert them to NSStrings and do a string comparison. 另一个对我有用的解决方案是将它们转换为NSStrings并进行字符串比较。 This was because I am working with 4 digit decimal prices and this worked well for me. 这是因为我正在使用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];
}

This got rid of the issue where I was comparing a double with 2 trailing zeros and a float with none. 这摆脱了我在比较一个double和2个尾随零和一个没有浮动的问题。

I'd normally warn against string comparisons like this but as my rules were always fixed it was OK for me in this case. 我通常会警告这样的字符串比较但是因为我的规则总是被修复,所以在这种情况下我可以。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM