比较 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。 在比较NSNumber
和 NSDecimalNumber
时,这似乎是一个精度问题。如果两个值都是NSDecimalNumber
,您将得到正确的结果。如果数字中的数字少一位,则使用当前代码得到正确的结果。
对不起,我的意思是如果数字少 3 位,您当前的代码就可以了。这意味着它适用于 16 位数字,但不能更多。这本质上是double
的精度,所以看起来在比较NSNumber
时,比较的值是double
。
【参考方案1】:
分析
NSDecimalNumber
的compare:
(由isEqualToNumber:
调用)检查其参数(此处为@(6560601600245628934)
)是否为另一个NSDecimalNumber
,如果不是,则使用NSNumber
的compare:
(@ 987654329@ 是NSDecimalNumber
的超类)。
作为NSNumber
d1
将其类型报告为double
- 即CFNumberGetType()
返回kCFNumberDoubleType
- 并且给定double
NSNumber
的compare:
将两个值比较为@987654339 @。
您的第一个值 (decimalNumberWithString:@"6560601600245628933"
) 以 NSDecimalNumber
的形式存储而不会造成重大损失,但其有效数字比 double
多,并且转换会丢失精度。
NSNumber
将您的第二个值 (@(6560601600245628934)
) 存储为 64 位整数 (kCFNumberSInt64Type
) 不会丢失精度,但转换为 double 时会丢失精度。
在您尝试过的变体中,表示为double
时的两个数字是相同的。
解决方法
变化:
@(6560601600245628934)
到:
[NSDecimalNumber numberWithLongLong:6560601600245628934LL]
直接从您的整数创建NSDecimalNumber
值。这将导致isEqualToNumber:
的两个参数都是NSDecimalNumber
,并且在比较时不会丢失任何精度。
错误或功能?
它可能被归类为 文档 错误,因为我还没有发现任何地方都记录了它(这远非结论性!)将与 double
s 进行比较。在bugreport.apple.com 提交错误报告,看看他们怎么说。
【讨论】:
以上是关于比较 NSDecimalNumber 和 NSNumber 得到错误的结果的主要内容,如果未能解决你的问题,请参考以下文章
NSNumberFormatter、NSDecimalNumber 和科学记数法
核心数据和 iPhone 的 NSDecimalNumber 问题