BlackWasp
Algorithms
.NET 1.1

Convert a Number into Words

When developing commercial applications, particularly when those applications perform activities such as cheque printing, it can be necessary to display numeric values using the equivalent text.  This article describes an algorithm to achieve this.

Algorithm Goals

There are many occasions when it is necessary to output a numeric value using text rather than numeric digits.  A common example is the production of cheques.  These usually require both the numeric and the spoken-word equivalent text to be printed.  Unfortunately, the .NET framework does not provide a simple method by which this conversion can be made so a custom algorithm must be developed.

This article describes an algorithm that converts a numeric value into the equivalent text.  It goes on to provide the C# code for this task.  For simplicity, the article concentrates on the generation of English words for 32-bit integer values.  However, you can extend the method easily to add other languages, decimal number conversion and currency information.  Alternatively, you can support the BlackWasp web site by purchasing and downloading our Number to Words Component that supports additional functionality, larger numbers and greater customisation from only £2.00.

Large Number Names

The English names of the smaller numbers are generally accepted.  However, once a number to be converted reaches a value of one thousand million or greater, the names of numbers vary according to usage.  The algorithm described in this article uses the modern short scale numbering.  In this scale, one thousand million is equivalent to one billion.  This is different to the less-used traditional British long scale numbering where a billion is one million million.

The following table lists the short scale number names included in the algorithm and their numeric values.  The range of numbers has been selected to provide for the full range of values that may be held in a 32-bit integer.  If you wish to extend the functionality of the algorithm to larger numbers, a Wikipedia article exists that lists the names of large numbers.

ValueName
1one
10ten
100hundred
1,000thousand
1,000,000million
1,000,000,000billion

Number Rules

The set of rules for converting an integer number to text initially appear to be reasonably complex.  However, once analysed, they can be separated into a small number of distinct rule groups that are simpler to implement individually. To provide a single algorithm for all integer values there are six such rule groups:

  • Zero Rule.  If the value is zero then the number in words is 'zero' and no other rules apply.
  • Three Digit Rule.  The integer value is split into groups of three digits starting from the right-hand side.  Each set of three digits is then processed individually as a number of hundreds, tens and units.  Once converted to text, the three-digit groups are recombined with the addition of the relevant scale number (thousand, million, billion).
  • Hundreds Rules.  If the hundreds portion of a three-digit group is not zero, the number of hundreds is added as a word.  If the three-digit group is exactly divisible by one hundred, the text 'hundred' is appended.  If not, the text "hundred and" is appended.  eg. 'two hundred' or 'one hundred and twelve'
  • Tens Rules.  If the tens section of a three-digit group is two or higher, the appropriate '-ty' word (twenty, thirty, etc.) is added to the text and followed by the name of the third digit (unless the third digit is a zero, which is ignored).  If the tens and the units are both zero, no text is added.  For any other value, the name of the one or two-digit number is added as a special case.
  • Recombination Rules.  When recombining the translated three-digit groups, each group except the last is followed by a large number name and a comma, unless the group is blank and therefore not included at all.  One exception is when the final group does not include any hundreds and there is more than one non-blank group.  In this case, the final comma is replaced with 'and'.  eg. 'one billion, one million and twelve'.
  • Negative Rule.  Negative numbers are always preceded by the text 'negative'.

The Algorithm and the Code

Preparation

Depending upon how you wish to use this algorithm, you may want to create it in a new class, incorporate it in an existing class or simply hide it behind a form event.  To keep the code simple and short, this article assumes you have a class of some kind prepared and are adding a new method to that class.  The code simply declares a new public method that accepts the integer to be converted as its only parameter.

To prepare, create or identify the class that you will add the method to, then add the following declaration code:

// Converts an integer value into English words
public string NumberToWords(int number)
{

}

NB: If you are creating or adding to a utilities class, you may wish to adjust the declaration to make the method static.  If you do, all of the other declarations detailed below must also be marked as static.

Declaring the Names for Numbers

There are three key groups of names for numbers that must be declared.  Firstly there are the small numbers between zero and nineteen that are used by the tens rule.  The second group of numbers is used to represent the tens values from twenty to ninety.  Finally we have the scale numbers.  These are the large numbers that are used during recombination.

The number names must be stored within the program code in a manner that permits easy identification.  In this example code, we will declare three arrays.  An array of twenty items holds the small numbers, an array of ten items is used for the tens values (with two initial unused entries) and an array of four values contains the scale number names with the first entry containing an empty string.

As the code will eventually include private methods in addition to the public method already declared, the arrays are created as private class-level variables.  This allows the arrays to be accessible by the entire class without being passed between functions.  The numbers are defined in Title Case as this is aesthetically pleasing and can be easily converted to upper or lower case after the process is complete.

Add the following declarations to the class to declare and initialise the arrays:

// Single-digit and small number names
private string[] _smallNumbers = new string[]
    {"Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight",
     "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen",
      "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
     
// Tens number names from twenty upwards
private string[] _tens = new string[]
    {"", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty",
     "Ninety"};
     
// Scale number names for use during recombination
private string[] _scaleNumbers = new string[]
    {"", "Thousand", "Million", "Billion"};

Applying the Zero Rule

The zero rule is the easiest to implement.  All that is required is a simple if statement to test for a value of zero in the integer parameter.  If the value is zero, the text returned is 'Zero' (from item 0 in the small numbers array).  In all other cases, the remaining rules must all be considered.

To implement the zero rule, the following code is added within the NumberToWords method:

// Zero rule
if (number == 0)
    return _smallNumbers[0];

Separating the Number into Three-Digit Groups

Once it has been determined that the value is non-zero, it is necessary to split the number into its three-digit groups.  This is relatively easy using a mixture of the modulus (%) and division (/) operators.  When any integer is divided by one thousand, the remainder supplied by the modulus operator is equivalent to the last three digits of the original value.  This can be repeated to extract the three-digit groups working from right to left.

One problem with this method is that if the original value is negative the modulus values will also be negative.  This can be rectified by using the Abs method from the standard Math class.  This method converts negative values to their positive equivalents but leaves positive values unchanged.

The range provided by the integer data type ensures that there will never be more than four three-digit groups.  We can therefore create an array with four elements to hold the extracted values.  Theis array is populated using a simple for loop that executes once for each group.

The separation is achieved by adding the following code after the zero rule:

// Array to hold four three-digit groups
int[] digitGroups = new int[4];

// Ensure a positive number to extract from
int positive = Math.Abs(number);

// Extract the three-digit groups
for (int i = 0; i < 4; i++)
{
    digitGroups[i] = positive % 1000;
    positive /= 1000;
}

NB: The three-digit group array index numbers can be matched against the large number array indices.  This will be useful for recombination later.

Converting a Three-Digit Group

The next rules that need to be applied are those concerning the individual three-digit groups.  Each group in turn needs to be converted to text.  To achieve this with efficient code, a private method is to be created.  This method, named ThreeDigitGroupToWords, accepts an integer value of no more than three digits and returns the equivalent English text.

The new subroutine is called once for each of the three-digit groups using another for loop.  For each pass, the returned text is stored in a new array of strings.  The index numbers of the three-digit group values and their new text versions are kept synchronised.

Add the following code after the digit extraction loop to call the new private method:

// Convert each three-digit group to words
string[] groupText = new string[4];

for (int i = 0; i < 4; i++)
    groupText[i] = ThreeDigitGroupToWords(digitGroups[i]);

To declare the new method, add the following declaration to the class:

// Converts a three-digit group into English words
private string ThreeDigitGroupToWords(int threeDigits)
{

}

Applying The Hundreds Rules

In order to apply the hundreds rules, there are two key values that must be known.  The first value is the number of hundreds that exist in the three-digit group.  This can be determines by simply dividing the three-digit value by one hundred.  The second number that is required is the remainder of this division.  This value, extracted using the modulus operator, represents the other two digits in the group.

The first of the hundreds rules determines if the number of hundreds is included in the returned text.  A simple if statement checks if this value is non-zero and adds text accordingly.  Otherwise, the digit is converted to text, using the appropriate string from the small numbers array, and the word 'Hundred' is appended.

A second conditional statement determines if there are any tens or units within the three-digit group.  If there are, these will be applied by the tens rules later.  However, the word 'and' will be required between the hundreds and the tens and units and this is added immediately.  Where the three-digit group represents an exact number of hundreds only, the remainder is zero and the 'and' is note required.

This code is added within the ThreeDigitGroupToWords method to apply the hundreds rules:

// Initialise the return text
string groupText = "";

// Determine the hundreds and the remainder
int hundreds = threeDigits / 100;
int tensUnits = threeDigits % 100;

// Hundreds rules
if (hundreds != 0)
{
    groupText += _smallNumbers[hundreds] + " Hundred";

    if (tensUnits != 0)
        groupText += " and ";
}

Applying The Tens Rules

To apply the tens rules we must first determine the number of tens and units.  This is achieved in a similar manner to determining the hundreds in the previous rules; the remainder from the previous rule is split using division and modulus operators.  Once the number of tens and units are known, several nested if statements are used to add the correct text.

This code should be added after the hundreds rules to apply the tens rules and return the result:

// Determine the tens and units
int tens = tensUnits / 10;
int units = tensUnits % 10;

// Tens rules
if (tens >= 2)
{
    groupText += _tens[tens];
    if (units != 0)
        groupText += " " + _smallNumbers[units];
}
else if (tensUnits != 0)
    groupText += _smallNumbers[tensUnits];

return groupText;

Recombining the Three-Digit Groups

Now that the three-digit groups have all individually been converted into words, they must be recombined.  This is achieved by starting with the lower value groups and adding each larger group to the combined string in turn.  Where a group is not blank, the correct large number text from the large numbers array is appended.  If a non-blank group has already been added, a comma or the word 'and' is also appended.

To control the decision of whether to add 'and' to the combined string, a Boolean variable is initialised after adding the first (smallest) three-digit group.  If this group represents a number between one and ninety nine, the Boolean value is set to true, otherwise is it set to false.  Whenever another group of digits is added that is non-zero the Boolean is checked.  If true, the word 'and' is added.  If the Boolean is false, a comma is appended instead.

To implement the recombination rules add the following code after the for loop in the public NumberToWords method:

// Recombine the three-digit groups
string combined = groupText[0];
bool appendAnd;

// Determine whether an 'and' is needed
appendAnd = (digitGroups[0] > 0) && (digitGroups[0] < 100);

// Process the remaining groups in turn, smallest to largest
for (int i = 1; i < 4; i++)
{
    // Only add non-zero items
    if (digitGroups[i] != 0)
    {
        // Build the string to add as a prefix
        string prefix = groupText[i] + " " + _scaleNumbers[i];
        
        if (combined.Length != 0)
            prefix += appendAnd ? " and " : ", ";
        
        // Opportunity to add 'and' is ended
        appendAnd = false;

        // Add the three-digit group to the combined string
        combined = prefix + combined;
    }
}

Applying the Negative Rule

The method is now almost complete.  The remaining process is to apply the negative rule.  To achieve this, the original value is rechecked to determine if it is positive or negative.  If negative, the text is prefixed accordingly.  The text is then returned from the method using a standard return statement.

Add the final lines of code to the NumberToWords method:

// Negative rule
if (number < 0)
    combined = "Negative " + combined;

return combined;

Downloadable Component

You can support the BlackWasp web site by purchasing and downloading our Number to Words Component.  This component supports additional functionality, larger numbers and greater customisation from only £2.00.  For more details, please follow the link.

Link to this Page2 February 2007
RSS RSS Feed
81 users on-line