Using Number Formatters

The NSNumberFormatter is one of those useful Foundation classes that is easy to forget about. It has a simple purpose of allowing you to flexibly convert backwards and forwards between numbers (NSNumber) and localised string (NSString) representations of those numbers. This post is a collection of quick notes for some of the most common uses when converting a number to a string.

NSNumberFormatterStyle

If you have very simple requirements you can avoid having to allocate an NSNumberFormatter and use a class method localizedStringFromNumber:numberStyle: with some predefined styles. The basic approach assuming we have an NSNumber initialised with the double value 1234567.8369:

NSNumber *num1 = [NSNumber numberWithDouble:1234567.8369];
NSString *numberStr = [NSNumberFormatter localizedStringFromNumber:num1 numberStyle:style];

where style is an NSNumberFormatterStyle constant. What follows is the output for each of the pre-defined styles assuming the English (en_GB) locale.

No Style

// No Style - NSNumberFormatterNoStyle
// 1234568

Decimal Style

Uses a grouping separator for thousands and three significant digits after the decimal separator:

// Decimal Style - NSNumberFormatterDecimalStyle
// 1,234,567.837

Currency style

Prefixes the currency symbol for the locale and uses two significant digits:

// Currency Style - NSNumberFormatterCurrencyStyle
// £1,234,567.84

Percent Style

The number is multiplied by 100 and shown with the percent symbol suffix:

// Percent Style - NSNumberFormatterPercentStyle
// 123,456,784%

Scientific Style

Shown in scientific notation:

// Scientific Style - NSNumberFormatterScientificStyle
// 1.2345678369E6

Spell Out Style

The number is spelt out in full for the current locale:

// Spell Out Style - NSNumberFormatterSpellOutStyle
// one million two hundred thirty-four thousand five hundred
// sixty-seven point eight three six nine

Locales

A big advantage of using the NSNumberFormatter is that it takes care of formatting the output based on the user locale. So with the device locale set to Spanish the previous examples become:

// Style                             en_GB           es_ES
// NSNumberFormatterNoStyle          1234568         1234568
// NSNumberFormatterDecimalStyle     1,234,567.837   1.234.567,837
// NSNumberFormatterCurrencyStyle    £1,234,567.84   € 1.234.567,84
// NSNumberFormatterPercentStyle     123,456,784%    123.456.784%
// NSNumberFormatterScientificStyle  1.2345678369E6  1,2345678369E6

Note how the currency symbol changes and that the comma separator and decimal point are reversed. The spell out style in the Spanish locale gives the following:

// un millón doscientos treinta y cuatro mil quinientos
// sesenta y siete coma ocho tres seis nueve

Custom Formatters

When the predefined styles are not sufficient for your needs you can create a custom number formatter:

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

You can still set the basic number style using one of the predefined styles. So for a custom decimal number format you would start off with:

[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];

There is a long list of properties for the number formatter that allow you to customise the output. So for example if I want to limit the displayed number to two decimal places and round the number down instead of up:

[numberFormatter setMaximumFractionDigits:2];
[numberFormatter setRoundingMode:NSNumberFormatterRoundDown];

When you want to obtain the NSString representation of the number you use stringFromNumber: passing the NSNumber as the argument:

// 1,234,567.83
NSString *text = [numberFormatter stringFromNumber:num1];

For fine grained control you can set a format string for both positive and negative numbers. So if I wanted negative numbers to be displayed in brackets instead of with a negative symbol:

NSNumber *num2 = [NSNumber numberWithDouble:-55.28];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[numberFormatter setNegativeFormat:@"(0.00)"];

// (55.28)
NSString *text = [numberFormatter stringFromNumber:num2];

The syntax for the format string is defined by Unicode Technical Standard 35 and is a little cryptic. The pattern consists of a prefix, numeric part and a suffix. The prefix and suffix allow you to specify currency and percentage symbols. The numeric part specifies separators (grouping and decimal) and the number of digits to use. To specify a number use either a # or 0 depending on whether you want to zero pad or not. Some examples should make it clear:

// maximum 2 fractional digits with no padding
12.3456  using  0.##  ==>  12.35
1.5      using  0.##  ==>  1.5

// maximum 2 fractional digits with zero padding
12.3456  using  0.00  ==>  12.35
1.5      using  0.00  ==>  1.50

// thousands separator
1500     using #,##0.00  ==> 1,500.00

Note that the grouping separator (,) and decimal separator (.) are replaced by the locale equivalent in the output string.

Currency Symbols

A quick tip if you are using a format string to create a currency number format. When you want to specify the position of the currency symbol in the prefix or suffix of the format string you need to use the Unicode currency symbol ¤ (\u00A4). The easiest way I have found to get this character is to use the character viewer. From the Finder, select Show Special Characters... from the Edit menu. If the Unicode category is not already shown you will need to add it using the settings menu from within the character viewer:

Scroll down the list until you see the Code Tables group, open the group and select the Unicode category:

You then need to select the character with the code 00A4 from the table or type a4 into the search bar. With the currency sign symbol selected you can then add it to the favorites for easy access. Double click the character to insert it at the current cursor position.

To finish up here is an example of a custom currency format using the currency symbol:

NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setPositiveFormat:@"¤#,##0.00"];
[currencyFormatter setNegativeFormat:@"(¤0.00)"];

Using this number formatter would give the following results in the en_GB locale:

1.5       ==>  £1.50
1500      ==>  £1,500.00
-1.56789  ==>  (£1.57)