Currency & High Precision Numbers With NSDecimalNumber

Thu, Mar 21

NSDecimalNumber is ideal if you need to work with numbers representing monetary values or those times when you can’t afford to lose precision in your calculations.

NSDecimalNumbers are expressed as a sign (+ -) a mantissa and exponent.

For example, 199.08 is as follows:

sign: +
mantissa: 19908
exponent: 10^-2 (ie 10 raised to the -2)

Here are two ways to define 199.08:

NSDecimalNumber *value1 = [NSDecimalNumber decimalNumberWithMantissa:19908
                                          exponent:-2
                                          isNegative:NO];
 
NSDecimalNumber *value2 = [NSDecimalNumber decimalNumberWithString:@"199.08"];

Doing Arithmetic With NSDecimalNumber

All the basic arithmetic operations are supported: addition, subtraction, division, powers, etc

Let’s look at a few examples:

// Use strings to create NSDecimalNumber
NSDecimalNumber *stout = [NSDecimalNumber decimalNumberWithString:@"7.09"];
NSDecimalNumber *ipa = [NSDecimalNumber decimalNumberWithString:@"6.99"];
 
// Use decimal with mantissa to create NSDecimalNumber
NSDecimalNumber *happyHourDiscount = [NSDecimalNumber decimalNumberWithMantissa:95
                                          exponent:-2 isNegative:NO];
 
NSDecimalNumber *stoutDiscount = [stout decimalNumberByMultiplyingBy:happyHourDiscount];
NSDecimalNumber *ipaDiscount = [ipa decimalNumberByMultiplyingBy:happyHourDiscount];
 
NSLog(@"Stout discount price: %@\nIPA discount price: %@", stoutDiscount, ipaDiscount);

The output will be as shown:

Stout discount price: 6.7355
IPA discount price: 6.6405

Question is, when dealing with currency how can you manage the numbers after the decimal in a manner that makes sense?

Using NSDecimalNumber Behaviors For Rounding

Without a behavior defined, arithmetic operations round to the closest value. With behaviors you can specify your rounding preferences as well as the scale (digits after decimal point) – all this is accomplished with the NSDecimalBehaviors protocol.

For example, NSDecimalNumberHandler, which adopts the NSDecimalBehaviors protocol, is used below to request numbers be rounded up, with a scale of 2 (that is, 2 digits after the decimal):

NSDecimalNumberHandler *roundUp = [NSDecimalNumberHandler
                                 decimalNumberHandlerWithRoundingMode:NSRoundUp
                                 scale:2
                                 raiseOnExactness:NO
                                 raiseOnOverflow:NO
                                 raiseOnUnderflow:NO
                                 raiseOnDivideByZero:YES];
 
NSDecimalNumber *stoutDiscountWithRoundUp = [stout decimalNumberByMultiplyingBy:happyHourDiscount withBehavior:roundUp];
NSDecimalNumber *ipaDiscountWithRoundUp = [ipa decimalNumberByMultiplyingBy:happyHourDiscount withBehavior:roundUp];
 
NSLog(@"Stout discount price:%@\nIPA discount price:%@", stoutDiscountWithRoundUp, ipaDiscountWithRoundUp);

Now the output is:

Stout discount price: 6.74
IPA discount price: 6.65

In addition to rounding up, you can round down and round to closest possible value (two variations on this).

One comment

Good to know :)

by Smin Rana on Mar 21, 2013. Reply #

Leave a Comment