NSNumberFormatter and Objective-C Types
· iOS, Development · Tagged NSNumberFormatter, multiplier, Objective-C types, gotcha and bug
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 numberInCents
to @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 numberInCents
was 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: $99.99
.
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.