NSNumberFormatter将大(但可表示)整数解析为双精度

时间:2014-09-21 20:49:42

标签: objective-c parsing uint64

我一直在使用以下方法将NSString解析为NSNumber&#39>:

// (a category method on NSString)
-(NSNumber*) tryParseAsNumber {
  NSNumberFormatter* formatter = [NSNumberFormatter new];
  [formatter setNumberStyle:NSNumberFormatterDecimalStyle];
  return [formatter numberFromString:self];
}

我有测试验证这是否正常工作:

test(@"".tryParseAsNumber == nil);
...
test([(@NSUIntegerMax).description.tryParseAsNumber isEqual:@NSUIntegerMax]);
...

当我在iPhone 6上切换到测试时,最大值测试开始失败,可能是因为NSUInteger现在是64位而不是32位。格式化程序返回的值现在是双1.844674407370955e+19而不是uint64_t 18446744073709551615

是否存在一个内置方法,该方法完全适用于所有int64和unsigned int64,或者我是否必须自己实现?

2 个答案:

答案 0 :(得分:1)

+ [NSNumber numberWithLongLong:]
+ [NSNumber numberWithUnsignedLongLong:]

你试过这些吗?

修改

我完全不确定你最终会对NSNumber的实例做些什么,但请考虑NSDecimalNumber似乎完全符合您的要求:

NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:@"18446744073709551615"];
NSLog(@"%@", decNum);

产生:

2014-09-21 15:11:25.472 Test[1138:812724] 18446744073709551615

此处需要考虑另一件事:NSDecimalNumber"是" NSNumber,因为它是后者的子类。因此,无论您使用NSNumber做什么,都可以使用NSDecimalNumber

答案 1 :(得分:0)

trudyscousin's answer让我明白了。

NSDecimalNumber decimalNumberWithString:能够以完全精度进行解析,但它允许一些不良输入(例如“88ffhih”被解析为88)。另一方面,NSNumberFormatter numberFromString:总是检测到错误的输入,但却失去了精确度。他们有相反的弱点。

所以......就这样做。例如,这是一个应该解析可表示的NSUIntegers而不是其他任何东西的方法:

+(NSNumber*) parseAsNSUIntegerElseNil:(NSString*)decimalText {
    // NSNumberFormatter.numberFromString is good at noticing bad inputs, but loses precision for large values
    // NSDecimalNumber.decimalNumberWithString has perfect precision, but lets bad inputs through sometimes (e.g. "88ffhih" -> 88)
    // We use both to get both accuracy and detection of bad inputs
    NSNumberFormatter* formatter = [NSNumberFormatter new];
    [formatter setNumberStyle:NSNumberFormatterDecimalStyle];
    if ([formatter numberFromString:decimalText] == nil) {
        return nil;
    }
    NSNumber* value = [NSDecimalNumber decimalNumberWithString:decimalText];

    // Discard values not representable by NSUInteger
    if (![value isEqual:@(value.unsignedIntegerValue)]) {
        return nil;
    }

    return value;
}