Go to the first, previous, next, last section, table of contents.


A dedicated function to format numbers

We have seen that the structure returned by localeconv as well as the values given to nl_langinfo allow to retrieve the various pieces of locale specific information to format numbers and monetary amounts. But we have also seen that the rules underlying this information are quite complex.

Therefore the X/Open standards introduce a function which uses this information from the locale and so makes it is for the user to format numbers according to these rules.

Function: ssize_t strfmon (char *s, size_t maxsize, const char *format, ...)
The strfmon function is similar to the strftime function in that it takes a description of a buffer (with size), a format string and values to write into a buffer a textual representation of the values according to the format string. As for strftime the function also returns the number of bytes written into the buffer.

There are two difference: strfmon can take more than one argument and of course the format specification is different. The format string consists as for strftime of normal text which is simply printed and format specifiers, which here are also introduced using `%'. Following the `%' the function allows similar to printf a sequence of flags and other specifications before the format character:

The next part of a specification is an, again optional, specification of the field width. The width is given by digits following the flags. If no width is specified it is assumed to be @math{0}. The width value is used after it is determined how much space the printed result needs. If it does not require fewer characters than specified by the width value nothing happens. Otherwise the output is extended to use as many characters as the width says by filling with spaces. At which side depends on whether the `-' flag was given or not. If it was given, the spaces are added at the right, making the output right-justified and vice versa.

So far the format looks familiar as it is similar to printf or strftime formats. But the next two fields introduce something new. The first one, if available, is introduced by a `#' character which is followed by a decimal digit string. The value of the digit string specifies the width the formatted digits left to the radix character. This does not include the grouping character needed if the `^' flag is not given. If the space needed to print the number does not fill the whole width the field is padded at the left side with the fill character which can be selected using the `=' flag and which by default is a space. For example, if the field width is selected as 6 and the number is @math{123}, the fill character is `*' the result will be `***123'.

The next field is introduced by a `.' (period) and consists of another decimal digit string. Its value describes the number of characters printed after the radix character. The default is selected from the current locale (frac_digits, int_frac_digits, see see section Generic Numeric Formatting Parameters). If the exact representation needs more digits than those specified by the field width the displayed value is rounded. In case the number of fractional digits is selected to be zero, no radix character is printed.

As a GNU extension the strfmon implementation in the GNU libc allows as the next field an optional `L' as a format modifier. If this modifier is given the argument is expected to be a long double instead of a double value.

Finally as the last component of the format there must come a format specifying. There are three specifiers defined:

`i'
The argument is formatted according to the locale's rules to format an international currency value.
`n'
The argument is formatted according to the locale's rules to format an national currency value.
`%'
Creates a `%' in the output. There must be no flag, width specifier or modifier given, only `%%' is allowed.

As it is done for printf, the function reads the format string from left to right and uses the values passed to the function following the format string. The values are expected to be either of type double or long double, depending on the presence of the modifier `L'. The result is stored in the buffer pointed to by s. At most maxsize characters are stored.

The return value of the function is the number of characters stored in s, including the terminating NUL byte. If the number of characters stored would exceed maxsize the function returns @math{-1} and the content of the buffer s is unspecified. In this case errno is set to E2BIG.

A few examples should make it clear how to use this function. It is assumed that all the following pieces of code are executed in a program which uses the locale valid for the USA (en_US). The simplest form of the format is this:

strfmon (buf, 100, "@%n@%n@%n@", 123.45, -567.89, 12345.678);

The output produced is

"@$123.45@-$567.89@$12,345.68@"

We can notice several things here. First, the width for all formats is different. We have not specified a width in the format string and so this is no wonder. Second, the third number is printed using thousands separators. The thousands separator for the en_US locale is a comma. Beside this the number is rounded. The @math{.678} are rounded to @math{.68} since the format does not specify a precision and the default value in the locale is @math{2}. A last thing is that the national currency symbol is printed since `%n' was used, not `i'. The next example shows how we can align the output.

strfmon (buf, 100, "@%=*11n@%=*11n@%=*11n@", 123.45, -567.89, 12345.678);

The output this time is:

"@    $123.45@   -$567.89@ $12,345.68@"

Two things stand out. First, all fields have the same width (eleven characters) since this is the width given in the format and since no number required more characters to be printed. The second important point is that the fill character is not used. This is correct since the white space was not used to fill the space specified by the right precision, but instead it is used to fill to the given width. The difference becomes obvious if we now add a right width specification.

strfmon (buf, 100, "@%=*11#5n@%=*11#5n@%=*11#5n@",
         123.45, -567.89, 12345.678);

The output is

"@ $***123.45@-$***567.89@ $12,456.68@"

Here we can see that all the currency symbols are now aligned and the space between the currency sign and the number is filled with the selected fill character. Please note that although the right precision is selected to be @math{5} and @math{123.45} has three characters right of the radix character, the space is filled with three asterisks. This is correct since as explained above, the right precision does not count the characters used for the thousands separators in. One last example should explain the remaining functionality.

strfmon (buf, 100, "@%=0(16#5.3i@%=0(16#5.3i@%=0(16#5.3i@",
         123.45, -567.89, 12345.678);

This rather complex format string produces the following output:

"@ USD 000123,450 @(USD 000567.890)@ USD 12,345.678 @"

The most noticeable change is the use of the alternative style to represent negative numbers. In financial circles it is often done using parentheses and this is what the `(' flag selected. The fill character is now `0'. Please note that this `0' character is not regarded as a numeric zero and therefore the first and second number are not printed using a thousands separator. Since we use in the format the specifier `i' instead of `n' now the international form of the currency symbol is used. This is a four letter string, in this case "USD ". The last point is that since the left precision is selected to be three the first and second number are printed with an extra zero at the end and the third number is printed unrounded.


Go to the first, previous, next, last section, table of contents.