为什么longLongValue返回不正确的值

时间:2012-09-14 04:39:07

标签: objective-c

我有一个NSDictionary,其中包含一个值为4937446359977427944的密钥。我尝试将其值作为long long得到4937446359977427968返回?

NSLog(@"value1 = %@", [dict objectForKey"MyKey"]); // prints 4937446359977427944 

long long lv = [dict objectForKey:@"MyKey"] longLongValue];

NSLog(@"value2 = %lld", lv); // prints 4937446359977427968

这样做的:

NSLog(@"%lld", [@"4937446359977427944" longLongValue]); // prints 4937446359977427944

我认为它是某种圆整问题,因为低位似乎被清除了,我只是不知道如何阻止它(或者为什么会发生这种情况)。

使用NSJSONSerialization创建字典,JSON对象(正确地)包含"MyKey": 4937446359977427944条目,dict对象正确。

NSDictionary中的值是NSDecimalNumber

是什么东西在幕后被转换为浮动?

3 个答案:

答案 0 :(得分:2)

NSDecimalValue不存储为double,它是64位无符号整数尾数,基数为10的8位有符号整数指数,以及符号位。

问题是NSDecimalValue的确切值只能表示为...... NSDecimalValue

您可以使用方法doubleValue获得大约64位IEE754值。

当您尝试使用longLongValue时,您可以有效地将长度为int的结果转换为IEE754的近似值。

您可能会或可能不会将其视为NSDecimalValue实施中的错误(并最终提交雷达并要求Apple使用其他转换例程)。但严格来说,这不是一个错误:它是一个设计决定。

您应该将NSDecimalValue视为一种浮点小数。事实上,它与IEEE754称之为扩展精度浮点十进制数的软件实现非常相似,只是它不符合该定义(因为它没有指数)至少支持-6143和+6144之间的值,因为它不支持NANs和infinites。)

换句话说,它不是整数的扩展实现,它是double的扩展(但缺少NAN和infinites)实现。事实上,Apple本身只提供了对double的近似转换(暗示转换为long long int可能对于任何超过53位精度的值都是精确的),这不是错误。

您可能希望也可能不希望自己实施不同的转换(使用类别)。

另一种可能的观点是将问题视为您使用的JSon实现中的错误。但这也是值得商榷的:它给了你一个NSDecimalValue,这可以说是一个正确的表述。您使用NSDecimalValue操作负责对其进行任何转换。

答案 1 :(得分:1)

我不确定您是对一个简单的解决方案感兴趣,还是只是查看精度损失原因的细节。

如果您对一个简单的答案感兴趣:-[NSDecimalNumber description]会产生一个包含值的字符串,-[NSString longLongValue]会将字符串转换为long long

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

输出

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

最后的注释

[decimalNumber descriptionWithLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]]可能更可靠,因为您的应用支持多个区域设置。

答案 2 :(得分:0)

对于有兴趣快速解决问题的人,按照模拟文件正确答案:

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

结果:

  

2014-04-16 14:22:02.997 Tutorial4 [97950:303]某些数字lld:
  8204064638523577098

     

2014-04-16 14:22:02.998 Tutorial4 [97950:303]某些数字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] dict中的一些数字:
  8204064638523577098

     

2014-04-16 14:22:02.999 Tutorial4 [97950:303] dict中的一些数字   lld:8204064638523577344

     

2014-04-16 14:22:02.999 Tutorial4 [97950:303]正确解析lld:
  8204064638523577098