Home > .NET, Uncategorized, Visual Basic > Parsing large numbers to words (english)

Parsing large numbers to words (english)

I’ve occasionally seen a request for this functionality pop up in the forums over the years. A developer is given the task to write out in words a number the way it may be spoken by a person. Actually, a more common use for this functionality is with bank checks where we write out the amount in words. So $1,234.56 would be written out as One Thousand Two Hundred Thirty Four Dollars and Fifty Six Cents. It’s amusing that the people who search for this functionality in code are surprised and even annoyed that the .NET framework does not have this built in… as if the framework does not have enough stuff already, right? A while back I came up with a solution for this based on a forum request (see the revised code at the bottom). I thought I would share this here again as a separate blog post. Actually, I decided to upgrade the code a tad just for the sake of never leaving good enough alone 😉

 
''' <summary>
''' Parses a monetary or decimal value into its common English version if it were to be spoken as words. 
''' </summary>
''' <param name="numberAsString">A string representing the numeric value to be parsed to words.</param>
''' <exception cref="ArgumentException">Thrown if the value argument cannot be parsed into a decimal value
''' </exception>
''' <exception cref="ArgumentOutOfRangeException">Thrown if the numeric value of the argument 
''' exceeds the maximum value of one quintrillion</exception>
Public Function ParseMoneyToText(ByVal numberAsString As String, includeCurrencyText As Boolean) As String

    'Takes a string value that translates as a decimal value of up to one thousand quadrillion dollars.  

    'Note: Removing the comma for European currency will remove the decimal pointer.  

    Dim Zero As Char = "0"c 'a much-reused value
    Dim returnValue As New System.Text.StringBuilder()

    'NOTE:  Replace any expected extraneous characters with an empty string
    numberAsString = numberAsString.Replace(",", String.Empty).Replace("$", String.Empty)
    numberAsString = numberAsString.TrimStart(Zero)

    Dim decimalCount As Int32 = 0
    For x As Int32 = 0 To numberAsString.Length - 1

        If numberAsString(x).ToString = "." Then
            decimalCount += 1

            If decimalCount > 1 Then
                Throw New ArgumentException("The specified value does not parse to a monetary or numeric value.")
            End If

        End If

        If Not (Char.IsDigit(numberAsString(x)) Or numberAsString(x).ToString = ".") And Not (x = 0 And numberAsString(x).ToString = "-") Then
            Throw New ArgumentException("The specified value does not parse to a monetary or numeric value.")
        End If

    Next

    Dim parts() As String = numberAsString.Split("."c)

    If parts.Length > 1 Then
        If includeCurrencyText Then
            '(Optional rule) Truncate to two decimal places for currency .
            parts(1) = parts(1).Substring(0, 2).ToCharArray 'Truncates -- doesn't round.  
        End If
    End If

    Dim IsNegative As Boolean = parts(0).Contains("-")
    If parts(0).Replace("-", "").Length > 18 Then
        Throw New ArgumentOutOfRangeException("The Maximum value has been exceeded.")
    End If

    If IsNegative Then
        parts(0) = parts(0).Replace("-", "")
        returnValue.Append("Minus ")
    End If


    'If you feel the urge to expand into quintillion and beyond then just follow the pattern below.  
    If parts(0).Length > 15 Then
        returnValue.Append(HundredsText(parts(0).PadLeft(18, Zero).Substring(0, 3)) & "Quadrillion ")
        returnValue.Append(HundredsText(parts(0).PadLeft(18, Zero).Substring(3, 3)) & "Trillion ")
        returnValue.Append(HundredsText(parts(0).PadLeft(18, Zero).Substring(6, 3)) & "Billion ")
        returnValue.Append(HundredsText(parts(0).PadLeft(18, Zero).Substring(9, 3)) & "Million ")
        returnValue.Append(HundredsText(parts(0).PadLeft(18, Zero).Substring(12, 3)) & "Thousand ")
    ElseIf parts(0).Length > 12 Then
        returnValue.Append(HundredsText(parts(0).PadLeft(15, Zero).Substring(0, 3)) & "Trillion ")
        returnValue.Append(HundredsText(parts(0).PadLeft(15, Zero).Substring(3, 3)) & "Billion ")
        returnValue.Append(HundredsText(parts(0).PadLeft(15, Zero).Substring(6, 3)) & "Million ")
        returnValue.Append(HundredsText(parts(0).PadLeft(15, Zero).Substring(9, 3)) & "Thousand ")
    ElseIf parts(0).Length > 9 Then
        returnValue.Append(HundredsText(parts(0).PadLeft(12, Zero).Substring(0, 3)) & "Billion ")
        returnValue.Append(HundredsText(parts(0).PadLeft(12, Zero).Substring(3, 3)) & "Million ")
        returnValue.Append(HundredsText(parts(0).PadLeft(12, Zero).Substring(6, 3)) & "Thousand ")
    ElseIf parts(0).Length > 6 Then
        returnValue.Append(HundredsText(parts(0).PadLeft(9, Zero).Substring(0, 3)) & "Million ")
        returnValue.Append(HundredsText(parts(0).PadLeft(9, Zero).Substring(3, 3)) & "Thousand ")
    ElseIf parts(0).Length > 3 Then
        returnValue.Append(HundredsText(parts(0).PadLeft(6, Zero).Substring(0, 3)) & "Thousand ")
    End If

    Dim hundreds As String = parts(0).PadLeft(3, Zero)
    hundreds = hundreds.Substring(hundreds.Length - 3, 3)




    If CInt(hundreds) <> 0 Then
        If CInt(hundreds) < 100 AndAlso parts.Length > 1 Then
            returnValue.Append("and ")
        End If

        returnValue.Append(HundredsText(hundreds))

        If includeCurrencyText Then
            returnValue.Append("Dollar")
            If CInt(hundreds) <> 1 Then
                returnValue.Append("s")
            End If
        End If

        If parts.Length > 1 AndAlso CInt(parts(1)) <> 0 AndAlso includeCurrencyText Then
            returnValue.Append(" and ")
        End If

    Else
        If includeCurrencyText Then
            returnValue.Append(" No Dollars")
        End If

        If parts.Length > 1 AndAlso CInt(parts(1)) <> 0 Then
            If includeCurrencyText Then
                returnValue.Append(" and ")
            Else
                returnValue.Append(" point ")
            End If

        End If

    End If


    If parts.Length = 2 Then
        If CInt(parts(1)) <> 0 Then
            If includeCurrencyText Then
                returnValue.Append(HundredsText(parts(1).PadLeft(3, Zero)))
                returnValue.Append("Cent")

                If CInt(parts(1)) <> 1 Then
                    returnValue.Append("s")
                End If
            Else
                returnValue.Append(" point ")

                For Each number As String In parts(1)
                    returnValue.Append(Ones(CInt(number)) & " ")
                Next
            End If

        End If
    End If

    Return returnValue.ToString.TrimEnd

End Function

Private Tens As String() = {"Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}
Private Ones As String() = {"Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven",
                            "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen",
                            "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}

''' <summary>
''' Dedicated helper function to the ConvertMoneyToText function.
''' </summary>
Private Function HundredsText(ByVal value As String) As String

    Dim returnValue As New System.Text.StringBuilder
    Dim IsSingleDigit As Boolean = True

    If CInt(value(0).ToString) <> 0 Then
        returnValue.Append(Ones(CInt(value(0).ToString)) & " Hundred ")
        IsSingleDigit = False
    End If

    If CInt(value(1).ToString) > 1 Then
        returnValue.Append(Tens(CInt(value(1).ToString) - 1) & " ")
        If CInt(value(2).ToString) <> 0 Then
            returnValue.Append(Ones(CInt(value(2).ToString)) & " ")
        End If

    ElseIf CInt(value(1).ToString) = 1 Then
        returnValue.Append(Ones(CInt(value(1).ToString & value(2).ToString)) & " ")

    Else
        If CInt(value(2).ToString) <> 0 Then
            If Not IsSingleDigit Then
                returnValue.Append("and ")
            End If

            returnValue.Append(Ones(CInt(value(2).ToString)) & " ")
        End If

    End If

    Return returnValue.ToString

End Function
Advertisements
  1. February 3, 2013 at 2:01 pm

    This is and old problem given out. I am glad to a solution actually written out.

    Renee

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: