Change default locale behavior for currency formatting - ios

I am facing a strange problem. My app relies on a double conversion:
currency string -> number -> currency string
Basically, the single conversions are realized through the built-in localization functions of iOS. I recently found that the app does not work properly when the user uses CHF as currency. Apparently the default for this locale is to round all currency values to the nearest 5 cents. (eg. CHF 1.28 will become CHF 1.30, and 1.21 CHF will become CHF 1.20).
For a bunch of reasons it's easier for me to solve the formatting convention than solve the bug for that only locale.
Do you know a way to force the conversion to use a more detailed rounding approach (eg. 0.01 instead of 0.05) for every locale?
Thank you!

I found an interesting solution:
currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setGeneratesDecimalNumbers:YES];
[currencyFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
// This will force the rounding behavior:
[currencyFormatter setRoundingIncrement:[NSNumber numberWithFloat:0.01]];

Related

NSNumberFormatter stringFromNumber returning 1,000,000,000 for input 999999999

When I try to convert number 999999999 to string using NSNumberFormatter, I am getting wrong value. It returns 1,000,000,000 instead of 999,999,999.
Here is the code that I use.
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setUsesSignificantDigits:YES];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:[NSLocale systemLocale]];
[numberFormatter setCurrencySymbol:#""];
numberAsString = [numberFormatter stringFromNumber:[NSDecimalNumber decimalNumberWithString:#"999999999" ]];
NSLog(#"Currency String %#",numberAsString);
You specified to use significant digits, but didn't say how many to use. For example, if you add the following, you get the result you were expecting:
[numberFormatter setMaximumSignificantDigits:9];
So, if you're going to use significant digits, specify how many you want to use. Or don't use significant digits at all.
You enabled usesSignificantDigits. Default value for maximumSignificantDigits is 6. Which means every number with more than 6 significant digits will be rounded to have less than 6 significant digits.
999 999 999 has 9 significant digits. So it will be rounded to 1 000 000 000
You probably don't want to set usesSignificantDigits at all.
Here is a good explanation what significant digits do with NSNumberFormatter.

Remove trailing zeros of a double only when necessary

Im making a calculator app and I am using a double for the precision. I didn't want the extra zeros and the decimal to be displayed so I changed the place holder token to "%.f". Because I did this if I do a problem on the calculator that does involve decimals they don't show up.
My question is basically if there is any way to have the %.f be used only when its necessary. If there is no way, what do you recommend doing to make it so I only have a decimal when its actually needed. Thanks.
You probably want to use NSDecimarlNumber, but to answer your question, you can user NSNumberFormatter to format the output for a double.
double someNumber = 1.9;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSLog(#"%#",[formatter stringFromNumber:[NSNumber numberWithDouble:someNumber]]);
My output
2014-01-13 11:30:41.429 DTMTestApp[23933:907] 1.9
For a calculator consider using NSDecimalNumber combined with NSNumberFormatter. There are going to be lot's of problems using floating point. Consider: (2 / 3.000000001) * 3.000000001, the result will not necessarily be exactly 2.

How to get NSNumberFormatter currency style from ISOCurrencyCodes?

If I were to have EUR, or USD (both ISO currency codes), how could I make my NSNumberFormatter to properly format me a string like this?
For EUR code: 10.000,00 €
For USD code: $10,000.00
I could do this by setting localeIdentifier. But I really need to get it from the ISO currency code. Is this possible?
[numberFormatter stringFromNumber: [_property price]];
Thank you!
Use the setCurrencyCode: method of NSNumberFormatter.
[numberFormatter setCurrencyCode:#"EUR"];
or
[numberFormatter setCurrencyCode:#"USD"];
Keep in mind that even though the desired currency symbol will be used, the resulting currency value string is still going to be formatted based on the user's locale.

How to place a currency symbol prefix or postfix with a string?

I'm wondering if there is any official way to put a currency symbol to the left or to the right from the money amount, depending of the region and the currency itself?
For example, if it's a $ sign, it should be to the left of the amount, as $20. If it's, for instance, € sign, then it should go as 20€.
I've found a simple way, I just check with If statement if the currency symbol is $ and append it to the left or to the right to my NSString. But this looks a bit lame like a crutch method.
I was unable to find some more convenient way. Is there any?
Thank you.
Apples official way ......
You need to use NSNumbers and NSNumberFormatter to convert the value to a currency and return a string representing it. This can be used in our labels e.t.c. someDouble refers to the number you wish to convert to a currency format.
NSNumber *currencyValue = [NSNumber numberWithDouble:someDouble];
NSNumberFormatter *formater = [[NSNumberFormatter alloc] init];
[formater setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *currencyString = [formater stringFromNumber:currencyValue];
NOTE: you may need to play around with the setLocale to get it to display the correct currency for a given region.

NSDecimalNumber decimalNumberWithString: ignores current locale

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)
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:
NSDecimalNumber is just storage. Logging it does not reflect anything about locale.
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).
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:
NSDecimalSeparator in NSUserDefaults.
AppleLocale in NSUserDefaults.
NSLocaleCode in NSUserDefaults.
The value set for CFBundleDevelopmentRegion.
The Environment's LANG/LC_ALL/etc... values.
+[NSLocale systemLocale].
And obviously it is not +[NSLocale currentLocale], as this question stems from the fact that the current locale has no effect.

Resources