NSDecimalNumber decimalNumberWithString: ignores current locale
Asked Answered
N

1

10

According to the documentation, [NSDecimalNumber decimalNumberWithString:] should use the locale decimal separator:

Whether the NSDecimalSeparator is a period (as is used, for example, in the United States) or a comma (as is used, for example, in France) depends on the default locale.

But when I try it, this code:

NSLog(@"%@", [NSDecimalNumber decimalNumberWithString:@"100,1"]);
NSLog(@"%@", [NSDecimalNumber decimalNumberWithString:@"100,1" locale:NSLocale.currentLocale]);

Gives...

100
100.1

...as output on both iOS 5 and iOS 6. I've tried with Swedish and French as regional settings as both these countries use comma (,) as decimal separator.

Shouldn't the output be the same?

(I know I can use [NSDecimalNumber decimalNumberWithString:locale:] to force the behavior, so this question is not about finding an alternative, just if this is a bug or I'm doing something wrong)

Northamptonshire answered 20/3, 2013 at 20:48 Comment(0)
S
7

NSDecimalNumber is simply a storage class for number-type data. It's running a parser (NSNumberFormatter) on the string you pass it to create its number. The reason your second log statement works "better" is because the first one is using the default number format locale (it looks like it's en_US, but I can't verify this, see the edit blow for more information.) to parse, and "100,1" isn't a valid number so the "non-number" part gets stripped off. By specifying a locale that uses "," decimal separators it's capturing the full number properly.

When you NSLog() an NSDecimalNumber it's simply calling -description, which has no locale context and can print, more or less, whatever it wants.

If you want to print properly formatted numbers use NSNumberFormatter like so:

NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:@"100.1"];

NSLog(@"%@", number);

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];

[formatter setNumberStyle:NSNumberFormatterDecimalStyle];

NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"];

[formatter setLocale:locale];

NSLog(@"%@", [formatter stringFromNumber:number]);

Or, briefly

NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:@"100.1"];
NSLog(@"%@", [NSNumberFormatter localizedStringFromNumber:number numberStyle:NSNumberFormatterDecimalStyle]);

if you just want to use the current locale.

In summary:

  1. NSDecimalNumber is just storage. Logging it does not reflect anything about locale.
  2. In order to get NSDecimalNumber to store a number properly, its locale needs to match the locale of the expected input (-[NSLocale currentLocale] is a good choice here).
  3. In order to display numbers formatted correctly for a given locale, use NSNumberFormatter.

Edit:

Ok, I've done some more research on this.

In GNUStep it looks like it ends up using the value for NSDecimalSeparator in NSUserDefaults (from a quick browse of their code).

Doing some experimentation I've found that none of the following affect the default parsing behavior, as far as I can tell:

  1. NSDecimalSeparator in NSUserDefaults.
  2. AppleLocale in NSUserDefaults.
  3. NSLocaleCode in NSUserDefaults.
  4. The value set for CFBundleDevelopmentRegion.
  5. The Environment's LANG/LC_ALL/etc... values.
  6. +[NSLocale systemLocale].

And obviously it is not +[NSLocale currentLocale], as this question stems from the fact that the current locale has no effect.

Shirline answered 20/3, 2013 at 21:47 Comment(4)
The question is not about printing, its about parsing. "the first one is using the default number format locale (en_US) to parse" - My question is why en_US is default when my current locale is fr_FR. So when the documentation says the default locale depends, when does it differ from en_US if not when my current locale is fr_FR?Northamptonshire
The documentation doesn't make it clear what causes a locale to become the 'default' locale, although it does make it seem like it can change. My initial thought was that it may be related to the value set for CFBundleDevelopmentRegion which "Usually corresponds to the native language of the author" but changing that value didn't seem to affect any behavior. If the API or documentation is unclear the best thing to do is file a radar.Shirline
I've filed a documentation radar that I encourage you to duplicate (rdar://13520160, openradar.appspot.com/radar?id=2878409); I've also added a bit more information to my answer.Shirline
What if the number=100 and I need to print 100,00? This typically happens for currency...Mareah

© 2022 - 2024 — McMap. All rights reserved.