How to format currency values

Describes how to format currency values for current locale.

From 6.1 onward

From 6.1, the TLocale class provides the FormatCurrency() functions that can format currency values into a descriptor. The functions can handle both small and large values. They render the currency amount as text, using the current locale's formatting requirements. These include items such as:

  • the appropriate currency symbol

  • whether the currency symbol is placed before or after the currency amount

  • whether there is space between the currency amount and the currency symbol

  • the number of decimal places

  • the way a negative currency value is rendered.

There are four overloaded variants of FormatCurrency(), two of which are suitable for rendering currency values that fit into a TInt type, and the other two of which are suitable for rendering very large currency values that fit into a TInt64 type.

Both pairs of functions behave in the same way, and the following code fragments will also apply to the TInt64 variants.

The first code fragment uses the void FormatCurrency(TDes&,TInt); variant.

...
TLocale locale;

TInt amount;
TBuf<64> buffer;

locale.set();
locale.FormatCurrency(buffer,amount);
...

On return, the descriptor buffer contains the currency value formatted as text using the currency symbol and formatting appropriate to the current locale. Note, however, that the caller is responsible for ensuring that the descriptor buffer is large enough. Too small a buffer results in a USER 11 panic.

The second code fragment uses the void FormatCurrency(TDes&,TDesOverflow&,TInt); variant.


class TestOverflow : public TDesOverflow
    {
    void Overflow(TDes& aBufferThatOverflowed);
    };

void TestOverflow::Overflow(TDes& aBufferThatOverflowed)
    {
    _LIT(KTextOverflow,"Overflowed");
    aBufferThatOverflowed = KTextOverflow;
    }

...
TLocale locale;
TestOverflow ovfl;

TInt amount;
HBufC* bufPtr;

bufPtr = HBufC::NewL(10);

locale.set();
locale.FormatCurrency(*bufPtr,ovfl,amount);
...

In this fragment, if the formatted text cannot fit into the buffer, *bufPtr, then TestOverflow::Overflow() is called which simply sets the text "Overflowed" into the buffer. In practice, application code would probably perform more sophisticated recovery which might include re-allocating the buffer and re-trying the FormatCurrency() operation.

Before 6.1

Before 6.1, the FormatCurrency() function is not available and the procedure for formatting a currency value needs more code and explicitly uses more of the functionality of the TLocale class.

In the following example code fragments, the formatting is done by a function called foo(). This function takes a descriptor that, on return, holds the formatted currency value. The function assumes that the descriptor is big enough to hold the formatted text. The example holds the currency value as a TReal value.

foo() uses the AppendNum() and Append() descriptor functions to format and append the currency value and symbol to aBuffer using an object of class TRealFormat to determine the character representation of the currency value.

The code formats the following locale-dependent aspects of currency information:

  • the number of decimal places

  • the decimal and thousands separators (iPoint and iTriad)

  • whether triads are allowed or not (iTriLen)

  • An iTriLen value of 1 signifies that triads are allowed throughout the currency amount. A value of zero means that no triads are allowed.

void foo(TDes& aBuffer, TReal aCurrencyAmount)
    {
            // get system locale settings
    TLocale locale; 
    TRealFormat realFormat; 
    realFormat.iType=EFixed; 

            // convert number to the general form "nnn.ddd" where
            // "n" is the integer and "d" is the decimal portion.
            // iWidth is the maximum number of characters allowed to represent the number
    realFormat.iWidth=30; 
    realFormat.iDecimalPlaces=locale.CurrencyDecimalPlaces(); 
    realFormat.iPoint=locale.DecimalSeparator(); 
    realFormat.iTriad=locale.ThousandsSeparator();
    realFormat.iTriLen=(locale.CurrencyTriadsAllowed() ? 1 : 0);
    ...

Note the following points:

  • Use CurrencySymbolPosition() and CurrencySpaceBetween() to decide whether the currency symbol should be placed before or after the currency amount and whether or not to insert a space between the symbol and the amount.

  • Use CurrencyNegativeInBrackets() to decide whether or not negative currency amounts should be enclosed in brackets. If this is the case, and the value is negative, an open bracket is appended and any minus sign is removed by appending the currency value to the buffer as a negative value.

  • Use CurrencySpaceBetween() to decide whether or not to insert a space between the currency symbol and the currency value.

... 
_LIT(KTxtOpenBracket,"(");
_LIT(KTxtCloseBracket,")");
_LIT(KTxtSpace," ");
TCurrencySymbol symbol;

    // Get system wide currency symbol setting
symbol.Set(); 

    // Append an open bracket, if the amount is negative and the locale demands it. 
if ((aCurrencyAmount<0) && (locale.CurrencyNegativeInBrackets()))
    {
    aBuffer.Append(KTxtOpenBracket);
    }

    // Position the currency symbol before the currency value, and insert space between the
    // symbol and the amount if the locale demands them.
if (locale.CurrencySymbolPosition() == ELocaleBefore)
    { 
    aBuffer.Append(symbol);
    if (locale.CurrencySpaceBetween())
        {
        aBuffer.Append(KTxtSpace); 
        }
    }

    // Append negative currency value and remove the sign if the locale demands
    // negative values in brackets
if ((locale.CurrencyNegativeInBrackets()) && (aCurrencyAmount<0))
    {
    aBuffer.AppendNum(-aCurrencyAmount,realFormat);
    }
else
    {
    aBuffer.AppendNum(aCurrencyAmount,realFormat); 
    }

    // Position the currency symbol after the currency value, and insert space between the
    // amount and the symbol if the locale demands them.
if (locale.CurrencySymbolPosition() == ELocaleAfter)
    {
    if (locale.CurrencySpaceBetween())
        {
        aBuffer.Append(KTxtSpace);
        }
    aBuffer.Append(symbol);
    }

    // Finally, append the closing bracket, if the value is negative and
    // the locale demands brackets around negative values.
if ((aCurrencyAmount<0) && (locale.CurrencyNegativeInBrackets()))
    {
    aBuffer.Append(KTxtCloseBracket);
    }
}

Note that the currency symbol cannot be set using the TLocale class. Use User::SetCurrencySymbol() to do this.