1

I've been using the following method to parse NSString's into NSNumber's:

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

And I had tests verifying that this was working correctly:

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

The max-value test started failing when I switched to testing on an iPhone 6, probably because NSUInteger is now 64 bits instead of 32 bits. The value returned by the formatter is now the double 1.844674407370955e+19 instead of the uint64_t 18446744073709551615.

Is there a built-in method that succeeds exactly for all int64s and unsigned int64s, or do I have to implement one myself?

Craig Gidney
  • 17,763
  • 5
  • 68
  • 136

2 Answers2

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

Have you tried these?

EDIT

I'm not at all certain what it is you'd ultimately do with your instances of NSNumber, but consider that NSDecimalNumber seems to do exactly what you want:

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

which yields:

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

Here's another thing to consider: NSDecimalNumber "is a" NSNumber, as it's a subclass of the latter. So it would appear that, whatever you can do with NSNumber, you can do with NSDecimalNumber.

Extra Savoir-Faire
  • 6,026
  • 4
  • 29
  • 47
0

trudyscousin's answer allowed me to figure it out.

NSDecimalNumber decimalNumberWithString: is capable of parsing with full precision, but it lets some bad inputs by (e.g. "88ffhih" gets parsed as 88). On the other hand, NSNumberFormatter numberFromString: always detects bad inputs but loses precision. They have opposite weaknesses.

So... just do both. For example, here's a method that should parse representable NSUIntegers but nothing else:

+(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;
}
Community
  • 1
  • 1
Craig Gidney
  • 17,763
  • 5
  • 68
  • 136