Should I use NSDecimalNumber to work with money?

When I started coding my first application, I used NSNumber for cash, without thinking twice. Then I thought that maybe there were enough types to cope with my values. However, I was advised on the iPhone SDK forum to use NSDecimalNumber due to its excellent rounding capabilities.

Not being a temperament mathematician, I thought the mantissa / exponent paradigm might be redundant; still, googlin 'around, I realized that most of the money / currency talk in cocoa was passed on to NSDecimalNumber.

Please note that the application I'm working on will be internationalized, so the ability to calculate the amount in cents is not really viable, because the monetary structure is highly dependent on the language used.

I am 90% sure that I need to go with NSDecimalNumber, but since I did not find a definite answer on the Internet (something like: "if you are dealing with money, use NSDecimalNumber!") I thought I would ask here. Perhaps the answer is obvious to most, but I want to be sure before starting a massive redistribution of my application.

Convince me :)

+47
cocoa-touch rounding cocoa currency
Jan 07 '09 at 18:29
source share
6 answers

Marcus Zarra has a fairly clear position on this issue: "If you are dealing with currency at all, then you should use NSDecimalNumber." His article inspired me to look into NSDecimalNumber, and I was very impressed with it. IEEE floating point errors when working with 10th grade math annoyed me for a while (1 * (0.5-0.4-0.1) = -0.00000000000000002776) and NSDecimalNumber fixes them.

NSDecimalNumber doesn't just add a few more binary precision floating point digits, it's actually base-10 math. This eliminates errors similar to those shown in the example above.

Now I am writing a symbolic mathematical application, so my desire for accuracy with an accuracy of 30 + decimal digits and the absence of strange floating point errors may be an exception, but I think it's worth a look. The operations are a bit more inconvenient than the simple math var = 1 + 2, but they are still controllable. If you are worried about distributing all kinds of instances during math operations, NSDecimal is equivalent to the C structure of NSDecimalNumber, and there are C functions for performing the same math operations with it. In my experience, this is fast enough for everyone except the most demanding applications (3 344 593 add-ons / s, 254 017 add-ons / s on MacBook Air, 281 555 add-ons / s, 12 027 add-ons / s on iPhone).

As an added bonus, the NSDecimalNumber descriptionWithLocale: method provides a string with a localized version of the number, including the correct decimal separator. The same thing happens in reverse order for its initWithString: locale: method.

+60
Jan 07 '09 at 22:46
source share

Yes. You have to use

NSDecimalNumber and

not double or float when working with currency on iOS.

Why is this?

Since we don’t want to get things like $ 9.9999999998 instead of $ 10

How does this happen?

Floats and doublings are approximations. They always have a rounding error. Computer formats used to store decimal places cause this taxi error. If you need more information, read

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

According to apple docs

NSDecimalNumber is an immutable subclass of NSNumber that provides an object-oriented wrapper for performing base-10 arithmetic. The instance can be any number that can be expressed as a mantissa x 10 ^ exponent, where the mantissa is a decimal integer up to 38 digits long, and the exponent is an integer from -128 to 127.wrapper to perform base-10 arithmetic.

Therefore, NSDecimalNumber is recommended for working with currency.

+8
Jun 14 '13 at 11:02
source share

(Adapted from my comment on another answer.)

Yes you need. The integrated amount of pennies only works until you need to imagine, say, half a cent. If this happens, you can change it to calculate half a cent, but what if you need to represent a quarter or an eighth?

The only correct solution is NSDecimalNumber (or something like that), which puts the problem at 10 ^ -128 ¢ (i.e., 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000001 ¢).

(Another way is arbitrary arithmetic, but this requires a separate library, such as the GNU MP Bignum library . Under LGPL. I have never used this library and I don’t know exactly how it works, so I can’t say how much it will work for you .)

[Edit: Apparently, at least one person - Brad Larson - thinks I'm talking about a binary floating point somewhere in this answer. I do not.]

+5
Jan 07 '09 at 22:26
source share

The best question is: when should you not use NSDecimalNumber to deal with money. The short answer to this question is that when you cannot tolerate the overhead of NSDecimalNumber and you don't need small rounding errors because you never deal with more than a few digits of precision. An even shorter answer: you should always use NSDecimalNumber when working with money.

+4
Jan 09 '09 at 19:50
source share

I found it convenient to use an integer to represent the number of cents, and then divide by 100 to represent. Avoids the whole problem.

+2
Jan 07 '09 at 18:37
source share

VISA, MasterCards and others use integer values ​​when passing amounts. For the sender and the recipient, it is correct to disassemble amouts in accordance with the currency indicator (divide or multiply by 10 ^ num, where num is the currency indicator). Please note that different currencies have different indicators. Usually this is 2 (therefore, we divide and multiply by 100), but some currencies have an indicator = 0 (VND, etc.) Or = 3.

+2
Apr 08 '15 at 3:23
source share



All Articles