Why is longLongValue returning the incorrect value

前端 未结 3 511
时光说笑
时光说笑 2021-01-20 11:01

I have a NSDictionary that contains a key with a value of 4937446359977427944. I try and get the value of it as a long long and get 4937446359977427968 back?



        
相关标签:
3条回答
  • 2021-01-20 11:34

    NSDecimalValue is not stored as a double, it's a 64 bits unsigned integer mantissa, an 8 bit signed integer exponent of base 10, and a sign bit.

    The problem is that an exact value of an NSDecimalValue is only representable as ... an NSDecimalValue.

    You can get an approximate 64 bits IEE754 value with method doubleValue.

    When you try to use longLongValue you effectively get the result of casting to a long long int the approximate IEE754 value.

    You may or may not consider it a bug in the implementation of NSDecimalValue (and eventually file a radar and ask Apple to use a different conversion routine). But strictly speaking this is not a bug: it's a design decision.

    You should think of NSDecimalValue as a sort of floating point decimal. In fact it's very similar to a software implementation of what IEEE754 would call an extended precision floating point decimal number, except that it does not conform to that definition (because it does not have an exponent supporting at least values between −6143 and +6144 and because it does not support NANs and infinites).

    In other words, it's not an extended implementation of an integer, it's an extended (but lacking NANs and infinites) implementation of a double. The fact that Apple natively only provides an approximate conversion to double (implying that the conversion to long long int may or may not be exact for any value that exceed 53 bits of precision) is not a bug.

    You may or may not want to implement a different conversion yourself (with a category).

    Another possible point of view is to consider the problem being a bug in the JSon implementation you used. But this is also highly debatable: it gave you a NSDecimalValue and that's arguably a correct representation. Either you operate with the NSDecimalValue or you are responsible for any conversion of it.

    0 讨论(0)
  • 2021-01-20 11:38

    For anyone interested in quick solution to the problem, as per Analog File proper answer:

    long long someNumber = 8204064638523577098;
    NSLog(@"some number lld:               %lld", someNumber);
    NSNumber *snNSNumber = [NSNumber numberWithLongLong:someNumber];
    NSLog(@"some number NSNumber:          %@", snNSNumber);
    NSString *someJson = @"{\"someValue\":8204064638523577098}";
    NSDictionary* dict = [NSJSONSerialization
     JSONObjectWithData:[someJson dataUsingEncoding:NSUTF8StringEncoding]
     options:0
     error:nil];
    NSLog(@"Dict: %@", dict);
    NSLog(@"Some digit out of dict:        %@", [dict objectForKey:@"someValue"]);
    NSLog(@"Some digit out of dict as lld: %lld", [[dict objectForKey:@"someValue"] longLongValue]);
    long long someNumberParsed;
    sscanf([[[dict objectForKey:@"someValue"] stringValue] UTF8String], "%lld", &someNumberParsed);
    NSLog(@"Properly parsed lld:           %lld", someNumberParsed);
    

    Results in:

    2014-04-16 14:22:02.997 Tutorial4[97950:303] some number lld:
    8204064638523577098

    2014-04-16 14:22:02.998 Tutorial4[97950:303] some number NSNumber:
    8204064638523577098

    2014-04-16 14:22:02.998 Tutorial4[97950:303] Dict: { someValue = 8204064638523577098; }

    2014-04-16 14:22:02.998 Tutorial4[97950:303] Some digit out of dict:
    8204064638523577098

    2014-04-16 14:22:02.999 Tutorial4[97950:303] Some digit out of dict as lld: 8204064638523577344

    2014-04-16 14:22:02.999 Tutorial4[97950:303] Properly parsed lld:
    8204064638523577098

    0 讨论(0)
  • 2021-01-20 11:42

    I'm not sure if your are interested in a simple solution or just looking into the details of why the loss of precision takes place.

    If you are interested in a simple answer: -[NSDecimalNumber description] products a string with the value, and -[NSString longLongValue] converts a string into a long long

    NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithString:@"4937446359977427944"];
    long long longLongNumber = [[decimalNumber description] longLongValue];
    NSLog(@"decimalNumber %@ -- longLongNumber %lld", decimalNumber, longLongNumber);
    

    outputs

    2014-04-16 08:51:21.221 APP_NAME[30458:60b] decimalNumber 4937446359977427944 -- longLongNumber 4937446359977427944
    

    Final Note

    [decimalNumber descriptionWithLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]] may be more reliable is your app supports multiple locales.

    0 讨论(0)
提交回复
热议问题