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

Posted

技术标签:

【中文标题】比较 NSDecimalNumber 和 NSNumber 得到错误的结果【英文标题】:Compare NSDecimalNumber to NSNumber gets wrong result 【发布时间】:2018-05-28 13:28:49 【问题描述】:

代码很简单,可以复现。

NSDecimalNumber *d1 = [NSDecimalNumber decimalNumberWithString:@"6560601600245628933"];
BOOL res = [d1 isEqualToNumber:@(6560601600245628934)];
NSLog(@"%@", @(res));

isEqualToNumber: 总是返回YES,即使我将d1@6560601600245628930 等其他数字进行比较...有什么想法吗?是bug吗?

【问题讨论】:

我认为这里正在对对象的类型进行比较。试试号码 6560601600245。 在比较 NSNumberNSDecimalNumber 时,这似乎是一个精度问题。如果两个值都是NSDecimalNumber,您将得到正确的结果。如果数字中的数字少一位,则使用当前代码得到正确的结果。 对不起,我的意思是如果数字少 3 位,您当前的代码就可以了。这意味着它适用于 16 位数字,但不能更多。这本质上是double 的精度,所以看起来在比较NSNumber 时,比较的值是double 【参考方案1】:

分析

NSDecimalNumbercompare:(由isEqualToNumber: 调用)检查其参数(此处为@(6560601600245628934))是否为另一个NSDecimalNumber,如果不是,则使用NSNumbercompare:(@ 987654329@ 是NSDecimalNumber 的超类)。

作为NSNumber d1 将其类型报告为double - 即CFNumberGetType() 返回kCFNumberDoubleType - 并且给定double NSNumbercompare: 将两个值比较为@987654339 @。

您的第一个值 (decimalNumberWithString:@"6560601600245628933") 以 NSDecimalNumber 的形式存储而不会造成重大损失,但其有效数字比 double 多,并且转换会丢失精度。

NSNumber 将您的第二个值 (@(6560601600245628934)) 存储为 64 位整数 (kCFNumberSInt64Type) 不会丢失精度,但转换为 double 时会丢失精度。

在您尝试过的变体中,表示为double 时的两个数字是相同的。

解决方法

变化:

@(6560601600245628934)

到:

[NSDecimalNumber numberWithLongLong:6560601600245628934LL]

直接从您的整数创建NSDecimalNumber 值。这将导致isEqualToNumber: 的两个参数都是NSDecimalNumber,并且在比较时不会丢失任何精度。

错误或功能?

它可能被归类为 文档 错误,因为我还没有发现任何地方都记录了它(这远非结论性!)将与 doubles 进行比较。在bugreport.apple.com 提交错误报告,看看他们怎么说。

【讨论】:

以上是关于比较 NSDecimalNumber 和 NSNumber 得到错误的结果的主要内容,如果未能解决你的问题,请参考以下文章

比较 NSDecimalNumber 不起作用?

NSDecimalNumber 值的比例和精度

NSNumberFormatter、NSDecimalNumber 和科学记数法

核心数据和 iPhone 的 NSDecimalNumber 问题

Swift 3 十进制、NSDecimal 和 NSDecimalNumber

如何在 NSDecimalNumber 中存储 1.66