NSNumberFormatter and Objective-C Types
On a recent project, I ran into some very unexpected behavior with
NSNumberFormatter. I was working with an API that delivered price information in cents. The price needed to be displayed in dollars. I decided to store the value in cents to maintain consistency with the API and to use
NSNumberFormatter to generate the string for the UI. It seemed like a great solution.
Here’s how it worked:
// Ninety-nine dollars in cents (this comes from the API) NSNumber *numberInCents = @9900; NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; [numberFormatter setMultiplier:@0.01]; NSString *formattedString = [numberFormatter stringFromNumber:numberInCents];
This generated the string
$99.00. So far, so good.
For this particular project, one of the requirements was to remove the fractional part of the formatted number if it was zero. I got that working, but I wanted to make sure that the fractional part was still displayed when necessary. To test it, I changed the value of
@9999. The result?
$99.00. What was going on?
After much confusion, I came across a question from someone with the same problem. The issue was that the Objective‑C type of
i which stands for
int. You can get the Objective-C type from any
NSValue—the superclass of
NSNumber—with the method
‑[NSValue objCType]. For some reason,
NSNumberFormatter takes this into account when applying the multiplier. The behavior is similar to what you would get when doing an integer division:
9999 / 100 = 99.
The workaround was to make sure that the Objective‑C type of the number was always a floating-point type:
// Ninety-nine dollars, ninety-nine cents in cents (this comes from the API) NSNumber *numberInCents = @9999; NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; [numberFormatter setMultiplier:@0.01]; // Ensure that the number to format has a floating-point Objective-C type NSNumber *floatingPointNumberInCents = @([numberInCents floatValue]); NSString *formattedString = [numberFormatter stringFromNumber:floatingPointNumberInCents];
This gave the expected result:
It is not clear whether this is the intended behavior for
NSNumberFormatter, so I filed a radar (rdar://16775214) to request clarification. Fortunately for now, the workaround is straightforward. Have you encountered any similar issues with
NSNumberFormatter? Let me know @a_hershberger on Twitter.