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

Posted

技术标签:

【中文标题】NSDecimalNumber(x).intValue 返回 -2、0、15 和 199,具体取决于 x 中的小数位数 (x = 199.999...5)【英文标题】:NSDecimalNumber(x).intValue returns -2, 0, 15 and 199, depending on the amount of decimals in x (x = 199.999...5) 【发布时间】:2019-05-08 08:34:21 【问题描述】:

我们在业务逻辑中发现了一个有趣的案例,它完全打破了我们的逻辑,我们不明白为什么 NSDecimalNumberDecimal 会这样。

我的案例如下:

import Foundation

let pQuantity = Decimal(string: "0.2857142857142857")!
let pPrice = Decimal(string: "7.00000000000000035")!

let calced = NSDecimalNumber(decimal: pQuantity * pPrice * Decimal(integerLiteral: 100))   // 200
let decimal = calced.decimalValue                                                          // 199.9999999999999999999999999999995
let integer = calced.intValue                                                              // 0

NSDecimalNumber(decimal: Decimal(string: "199.9999999999999999999999999999995")!).intValue // 0
NSDecimalNumber(decimal: Decimal(string: "199.9999999999999995")!).intValue                // 199
NSDecimalNumber(decimal: Decimal(string: "199.99999999999999995")!).intValue               // 15
NSDecimalNumber(decimal: Decimal(string: "199.999999999999999995")!).intValue              // -2

在上面的操场代码中,如果你不想自己运行,你可以向右滚动看到返回值。

我们需要将原始十进制值、数量和价格临时转换为整数,以计算将这些数量平均分配多少以产生美观的价格。但是,由于某种原因,在这种情况下我们不能,因为转换的初始步骤失败,产生 0 而不是 200(是的,当前代码会产生 199 这是一个错误)。

为什么 NSDecimalNumber 会根据小数位数返回这些奇怪的值,范围从 -2199

我们的解决方案是在将内部计算放入NSDecimalNumber 之前对其进行四舍五入,但我们首先想知道造成这种情况的原因。这是一个错误还是预期的,应该知道它可能会发生?

【问题讨论】:

很可能与这个错误bugs.swift.org/browse/SR-2980有关。 @MartinR 似乎是这里的明显候选人。我想在我们等待错误解决之前,除了四舍五入之外我无能为力? 在***.com/q/25705511 处观察到 NSDecimalNumber 的类似奇怪行为。 @MartinR 所以根据那个线程它是超类中的东西而不是覆盖访问器?有趣的是,在这种情况下它并没有这样做。 我不确定该问答的接受答案是否正确解释了它,但我没有花太多时间阅读它(因此我现在不会将其作为副本关闭)。我只是想提供另一个这种行为的例子。 – NSDecimalNumber 是 Apple 平台上(闭源)Foundation 框架的一部分,因此我只能建议在 Apple Bugreporter 上提交错误。 【参考方案1】:

这显然是一个基础错误,可能是 cmets 中的 the one mentioned by Martin R。

我在 Playground (Swift 5) 中进行了实验,发现关于 int32Value 正常工作的那个 bug 的评论是正确的。

import Foundation

let pQuantity = Decimal(string: "0.2857142857142857")!
let pPrice = Decimal(string: "7.00000000000000035")!

let calced = NSDecimalNumber(decimal: pQuantity * pPrice * Decimal(integerLiteral: 100))   // 200
let decimal = calced.decimalValue                                                          // 199.9999999999999999999999999999995
let integer = calced.int32Value                                                              // 200

NSDecimalNumber(decimal: Decimal(string: "199.9999999999999999999999999999995")!).uint32Value // 200
NSDecimalNumber(decimal: Decimal(string: "199.9999999999999995")!).int32Value                // 200
NSDecimalNumber(decimal: Decimal(string: "199.99999999999999995")!).int32Value               // 200
NSDecimalNumber(decimal: Decimal(string: "199.999999999999999995")!).int32Value              // 200

此外,正如您所见,uint32Value 也可以正常工作。但是,64 位变体都不起作用。

如果您确定您的结果适合Int32,您可以使用它作为解决方法,直到他们修复它,这可能永远不会,因为该错误已经存在一段时间了。

【讨论】:

出于我们的目的,我认为我们会在将其推入 NSDecimalNumber 之前对其进行舍入。但这是一个有趣的问题。我会接受这个答案,因为它显然是一个错误,解决方法可能取决于团队和他们需要什么。 @Simon 谢谢。是的,我认为最好在转换为整数之前进行舍入,因为您可以更好地控制舍入的发生方式,而且 intxxValue 的舍入行为似乎没有得到很好的记录。

以上是关于NSDecimalNumber(x).intValue 返回 -2、0、15 和 199,具体取决于 x 中的小数位数 (x = 199.999...5)的主要内容,如果未能解决你的问题,请参考以下文章

如果 NSDecimalNumber 大于 Uint64,如何将 NSDecimalNumber 转换为字节数组?

将 NSDecimalNumber 变为负数

为啥来自 NSString 分配的 NSDecimalNumber 会失败?

NSDecimalNumber 比较失败

对 NSDecimalNumber 有点困惑?

在 NSDecimalNumber 的索引处查找数字