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

当我开始编写我的第一个应用程序时,我使用NSNumber来获得金钱价值而不用两次思考。 然后我想,也许ctypes足以处理我的价值观。 然而,在iPhone SDK论坛中,我build议使用NSDecimalNumber,因为它具有出色的舍入function。

我不是一个气质的math家,我认为尾数/指数范式可能是矫枉过正的; 还是googlin“,我意识到大多数关于cocoa货币/货币的讨论都被称为NSDecimalNumber。

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

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

说服我 :)

Marcus Zarra在这方面有一个非常明确的立场: “如果你正在处理货币,那么你应该使用NSDecimalNumber。” 他的文章激励我研究NSDecimalNumber,我对它印象深刻。 在处理基数为10的math问题时,IEEE浮点错误一直令我恼火(1 *(0.5 – 0.4 – 0.1)= -0.00000000000000002776),NSDecimalNumber也不再支持它们。

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

现在,我写了一个符号math应用程序,所以我对30位十进制数字精度的要求和没有奇怪的浮点错误可能是一个例外,但我认为这是值得一看。 这个操作比简单的var = 1 + 2风格的math更笨拙,但是它们仍然可以pipe理。 如果你担心在math运算中分配各种各样的实例,NSDecimal是NSDecimalNumber的C结构等价物,还有C函数可以完成与它完全相同的math运算。 根据我的经验,除了要求最苛刻的应用程序之外,这些应用程序的速度都非常快。(MacBook Air上的增加速度为3,344,593次/秒,MacBook Air上为25,0,017次/秒,iPhone上增加了281,555次/秒,每秒钟12,027次)。

作为一个额外的好处,NSDecimalNumber的descriptionWithLocale:方法提供了一个string,其中包含数字的本地化版本,包括正确的小数点分隔符。 它的initWithString:locale:方法也是相反的。

是。 你必须使用

NSDecimalNumber

当您在iOS上处理货币时,不会翻倍浮动

这是为什么??

因为我们不想要$ 9.9999999998而不是$ 10

这是怎么发生的?

浮动和双打是近似值。 他们总是有一个舍入错误。 计算机用来存储小数的格式会导致这个路由错误。 如果你需要更多的细节阅读

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

根据苹果文件,

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

所以NSDecimalNumber被推荐用于处理货币。

(改编自我对另一个答案的评论。)

是的你应该。 只要你不需要代表半分,一分钱就可以工作。 如果发生这种情况,你可以把它改成半分钱,但如果你需要代表一分钱,还是八分之一分呢?

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

(另一种方法是任意精度algorithm,但是需要一个单独的库,比如GNU MP Bignum库 ,GMP在LGPL下,我从来没有用过这个库,不知道它是如何工作的,所以我不能说它对你有多好。)

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

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

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

VISA,MasterCard和其他人在传递金额时使用整数值。 由发货人和接收人根据货币指数正确parsing分配(除以或乘以10 ^ num,其中num – 是货币的指数)。 请注意不同的货币有不同的指数。 通常是2(因此我们除以乘以100),但是一些货币的指数= 0(VND等)或= 3。