NSDecimalNumber 返回错误的 uint64_t 值

Posted

技术标签:

【中文标题】NSDecimalNumber 返回错误的 uint64_t 值【英文标题】:NSDecimalNumber returning wrong uint64_t value 【发布时间】:2016-05-31 04:51:09 【问题描述】:

我的应用程序通过persistentId 记住MPMediaItems,就像一个人一样。它将 persistentId 保存在 JSON 文件中,稍后读取和解析该文件。 NSJSONSerialization 创建一个 NSDecimalNumber 来包含 Id,我的应用稍后在 MPMediaQuery 中使用它来获取 MPMediaItem。

我发现有时会发现错误的 MPMediaItem。当我再四处寻找时,我发现当我将 NSDecimalNumber 转换为 uint64_t 值时,[NSNumber unsignedLongLongValue] 返回了错误的值!

NSDecimalNumber* const songId = _songId;
uint64_t         const songIdValue = songId.unsignedLongLongValue;

songId is      1457249251113381177
songIdValue is 1457249251113381120
-or-
songId is      0x1439307919d12d39
songIdValue is 0x1439307919d12d00

什么鬼?看起来低字节在将此 NSDecimalNumber 转换为 uint64_t 时被清除。这是 NSDecimalNumber 的某种正确行为吗?我发现其中有错误吗?

我正在通过将 NSDecimalNumber 从 NSJSONSerialization 转换为字符串并使用 [NSString longLongValue] 将字符串转换为 uint64_t 来解决此问题。

这很可怕,因为我现在似乎必须检查我在 JSON 文件中存储大整数的每个地方,并确保我以这种 NSString-y 方式评估它们。

【问题讨论】:

我在这里找到了一条评论,说 NSDecimalNumber 在转换为 uint64_t 之前先转换为双精度数。因此,任何超过 53 的有效位都会被清除,正如我在这里看到的那样。令人难以置信的是,Apple 让这一切站稳了脚跟! NSJsonSerialization 在为整数而不是 NSNumber 选择此类时出错。我打算将我的 JSON 解析切换到 JSONKit,我建议其他人也这样做。 在他的评论中,Christopher 可能指的是this answer to NSDecimalNumber and large unsigned long long (64-bit) integers 【参考方案1】:

如果你想保持最大的精度,你可以:

仅适用于 NSDecimalNumbers,

当您将 NSDecimalNumber 的值传输到另一个不是 NSDecimalNumber 的变量时,请使用 NSString。 [NSDecimalNumber longLongValue] 或 [NSDecimalNumber doubleValue] 等方法不够具体。

NSDecimalNumber     *songId;
uint64_t            songIdValue;
NSString            *intermediateStr;

songId = [NSDecimalNumber decimalNumberWithString:@"1457249251113381177"];  
intermediateStr = songId.stringValue;
songIdValue = intermediateStr.longLongValue;

为什么使用字符串比其他方法更精确?我不知道。但是 songIdValue 包含正确的值,没有任何损失。

【讨论】:

以上是关于NSDecimalNumber 返回错误的 uint64_t 值的主要内容,如果未能解决你的问题,请参考以下文章

比较 NSDecimalNumber 和 NSNumber 得到错误的结果

从 NSDecimalNumber 中删除负号 (-) 给我一个错误

使用 numberFromString 生成 NSDecimalNumber 时如何在没有分数错误的情况下获得舍入值?

NSDecimalNumber(x).intValue 返回 -2、0、15 和 199,具体取决于 x 中的小数位数 (x = 199.999...5)

[NSDecimalNumber 保留]:消息发送到已释放实例 0x174222220,但为啥呢?

NSDecimalNumber 四舍五入在 iOS 11 中不起作用