English 中文(简体)
我应该使用NSDecimalNumber来处理金钱吗?
原标题:
  • 时间:2009-01-07 18:29:52
  •  标签:

当我开始编写我的第一个应用时,我毫不犹豫地使用 NSNumber 来处理货币值。 然后我想,也许 c 类型足以处理我的值。 然而,我在 iPhone SDK 论坛上被建议使用 NSDecimalNumber,因为它具有出色的舍入功能。

由于本人非数学家出身,因此我认为小数/指数的范例可能有些多余;但是,在搜索时,我发现关于货币/货币的大多数讨论都涉及到NSDecimalNumber。

请注意,我正在开发的应用程序将被国际化,因此按分计算金额的选项并不真正可行,因为货币结构在很大程度上取决于使用的区域设置。

我有90%的把握需要使用NSDecimalNumber,但由于在网上没有明确的答案(例如:“如果您处理货币,则使用NSDecimalNumber!”),所以我想在这里问一下。也许答案对于大多数人是显而易见的,但在重构我的应用程序之前,我想要确保。

说服我 :)

最佳回答

Marcus Zarra对此有一个非常明确的立场:“如果你有任何与货币相关的操作,那么你应该使用NSDecimalNumber。”他的文章激励了我去研究NSDecimalNumber,并且我对它非常印象深刻。在涉及基于十进制的数学计算时,IEEE浮点错误一直让我感到恼火(1 *(0.5 - 0.4 - 0.1)= -0.00000000000000002776),而NSDecimalNumber可以解决这个问题。

NSDecimalNumber 不仅增加了几位二进制浮点精度,而且实际上进行的是十进制数学运算。这可消除上述示例中所显示的错误。

现在,我正在编写一个符号数学应用程序,所以我对30+位小数精度和没有奇怪的浮点错误的渴望可能是一个例外,但我认为它值得一看。操作比简单的var = 1 + 2样式的数学略微繁琐,但仍然可以处理。如果您担心在数学运算过程中分配各种实例,NSDecimal是NSDecimalNumber的C结构等效物,并且有用于执行完全相同数学运算的C函数。根据我的经验,这些对于除了最苛刻的应用程序以外的所有应用程序都足够快(在MacBook Air上3,344,593次加法/秒,254,017次除法/秒,在iPhone上281,555次加法/秒,12,027次除法/秒)。

作为额外的奖励,NSDecimalNumber的descriptionWithLocale:方法提供了一个带有本地化版本的字符串,包括正确的小数分隔符。其initWithString:locale:方法也是如此。

问题回答

是的。你必须使用

NSDecimalNumber

在 iOS 处理货币时不要使用 doublefloat

Why is that??

因为我们不想得到像$9.9999999998这样的东西,而不是$10

How that happens??

Floats and doubles are approximations. They always comes with a rounding error. The format computers use to store decimals cause this rouding error. If you need more details read

将此翻译成中文:http://floating-point-gui.de/ http://floating-point-gui.de/

根据苹果文档,

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

因此,建议使用NSDecimalNumber来处理货币。

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

是的,你应该这样做。仅使用整数数量的便士只有在表示半分之一美分之类的金额时无法满足需要。如果发生这种情况,你可以更改为计算半分之一美分,但是如果你需要表示四分之一美分或八分之一美分怎么办?

The only proper solution is NSDecimalNumber (or something like it), which puts off the problem to 10^-128¢ (i.e.,
0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000001¢).

另一种方法是任意精度算术,但这需要一个单独的库,例如GNU MP Bignum库。 GMP受LGPL保护。 我从未使用过该库,也不知道它的确切工作原理,因此我无法确定它对你有多大帮助。

显然,至少有一人(Brad Larson)认为我在这个回答中谈论的是二进制浮点数。我并不是。

我发现使用整数表示美分数并且除以100进行展示很方便,避免了整个问题。

更好的问题是,什么时候不应该使用NSDecimalNumber来处理钱。那个问题的简短答案是,当您无法容忍NSDecimalNumber的性能开销并且您不关心小的舍入误差,因为您从未涉及超过几位数的精度时。更短的答案是,您应始终在处理钱时使用NSDecimalNumber。

VISA、MasterCards和其他公司在传递金额时使用整数值。根据货币指数正确解析金额是由发件人和收件人决定的(通过10的num次方进行除或乘,其中num是该货币的指数)。请注意,不同的货币具有不同的指数。通常是2(因此我们将其除以和乘以100),但某些货币的指数为0(VND等)或3。





相关问题
热门标签