我应该使用NSDecimalNumber来处理钱吗?

时间:2009-01-07 18:29:52

标签: cocoa cocoa-touch rounding currency

当我开始编写我的第一个应用程序时,我使用了NSNumber来获取金钱价值,而没有考虑过两次。然后我想也许c类型足以处理我的价值观。然而,我被推荐在iPhone SDK论坛中使用NSDecimalNumber,因为它具有出色的舍入功能。

不是气质的数学家,我认为尾数/指数范式可能过度;仍然,googlin'周围,我意识到大多数关于可可货币/货币的谈判被称为NSDecimalNumber。

请注意,我正在处理的应用程序将会国际化,因此以分数计算金额的选项并不可行,因为货币结构在很大程度上取决于所使用的语言环境。

我90%确定我需要使用NSDecimalNumber,但因为我在网上找不到明确的答案(例如:“如果你处理钱,请使用NSDecimalNumber!”)我想我会问这里。也许答案对大多数人来说是显而易见的,但我想在开始对我的应用程序进行大规模重新分解之前确定。

说服我:)。

6 个答案:

答案 0 :(得分:63)

Marcus Zarra对此有一个非常明确的立场:"If you are dealing with currency at all, then you should be using NSDecimalNumber."他的文章激励我研究NSDecimalNumber,我对它印象非常深刻。处理基数为10的数学时的IEEE浮点错误让我感到恼火一段时间(1 *(0.5 - 0.4 - 0.1)= - 0.00000000000000002776)并且NSDecimalNumber不再使用它们。

NSDecimalNumber不只是添加二进制浮点精度的另外几个数字,它实际上是基数为10的数学运算。这消除了上面例子中显示的错误。

现在,我正在编写一个符号数学应用程序,所以我对30+十进制数字精度和没有奇怪的浮点错误的渴望可能是一个例外,但我认为值得关注。这些操作比简单的var = 1 + 2样式数学更加尴尬,但它们仍然可以管理。如果您担心在数学运算期间分配各种实例,NSDecimal是NSDecimalNumber的C结构等价物,并且有C函数用于执行与它完全相同的数学运算。根据我的经验,对于除了最苛刻的应用程序之外的所有应用程序(3,344,593个/秒,MacBook Air上的254,017个分区/秒,iPhone上增加了281,555个分区/秒,12,027个分区/秒)的速度非常快。

作为一个额外的好处,NSDecimalNumber的descriptionWithLocale:方法提供了一个字符串,其中包含数字的本地化版本,包括正确的小数分隔符。对于其initWithString:locale:method。

,情况相反

答案 1 :(得分:8)

是。你必须使用

NSDecimalNumber

在iOS上处理货币时,

不是加倍浮动

为什么?

因为我们不希望获得 $ 9.9999999998 而非 $ 10

等内容

怎么回事?

浮点数和双打数是近似值。它们总是带有舍入误差。计算机用于存储小数的格式会导致此轮询错误。 如果您需要更多详细信息,请阅读

http://floating-point-gui.de/

根据苹果文档,

NSDecimalNumber是NSNumber的不可变子类,提供了一个面向对象的包装器,用于执行base-10算法。实例可以表示任何可以表示为尾数x 10 ^指数的数字,其中尾数是长度为38位的十进制整数,指数是-128到127.wrapper的整数,用于执行基数为10的算术。

因此推荐NSDecimalNumber处理货币。

答案 2 :(得分:5)

(改编自我对其他答案的评论。)

是的,你应该。只要您不需要代表半分钱就可以使用完整数量的便士。如果发生这种情况,您可以将其改为半美分,但如果您需要代表一个四分之一或八分之一美分呢?

唯一合适的解决方案是NSDecimalNumber(或类似的东西),它将问题推迟到10 ^ -128¢(即, 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000001¢)。

(另一种方式是任意精度算术,但这需要一个单独的库,例如GNU MP Bignum library。GMP属于LGPL。我从来没有使用过那个库,也不确切知道它是怎么回事工作,所以我不能说它对你有多好。)

[编辑:显然,至少有一个人 - 布拉德拉森 - 认为我在谈论这个答案的某个地方的二进制浮点数。我不是。]

答案 3 :(得分:4)

一个更好的问题是,你什么时候使用NSDecimalNumber来处理钱。这个问题的简短答案是,当你无法容忍NSDecimalNumber的性能开销时,你不关心小的舍入错误,因为你从不处理超过几位数的精度。更简短的答案是,你应该总是在处理钱时使用NSDecimalNumber。

答案 4 :(得分:2)

我发现使用整数来表示分数然后除以100进行演示是很方便的。避免整个问题。

答案 5 :(得分:2)

VISA,MasterCards和其他人在传递金额时使用整数值。它取决于发送者和接收者根据货币指数正确解析amouts(除以或乘以10 ^ num,其中num - 是货币的指数)。请注意,不同的货币具有不同的指数。通常它是2(因此我们除以并乘以100),但有些货币的指数= 0(VND等),或= 3.