2
\$\begingroup\$

A bit of code I made in VB.NET to convert the digit form of integers into words.

I've intentionally assumed that inputs will match the desired format since the focus of this task for me was to appropriately handle the logic required to complete such a task from valid input. Due to this, there is no error checking built into my getInputNumber function.

I'd appreciate any feedback on this.

Module NumberToWords

    Sub Main()
        Do While (True)
            Dim inputNumber As String = getInputNumber()

            Console.WriteLine(convertNumberToWords(inputNumber))
        Loop
    End Sub

    Function getInputNumber() As String
        Console.WriteLine("Enter a number:")

        Return Console.ReadLine()
    End Function

    Function convertNumberToWords(ByVal inputString As String) As String
        Const DIGITS_IN_HUNDRED As UInt16 = 3

        ' Add leading zeros so that number of digits is a multiple of 3.
        Do While (inputString.Length Mod DIGITS_IN_HUNDRED <> 0)
            inputString = "0" & inputString
        Loop

        Dim resultString As String = ""

        ' Process each set of 3 digits in the number, then add the appropriate magnitude indicator at the end (e.g. billion).
        For i = 0 To inputString.Length - 1 Step 3
            Dim hundreds As String = inputString(i)
            Dim tens As String = inputString(i + 1)
            Dim units As String = inputString(i + 2)

            If (hundreds <> "0" OrElse tens <> "0" OrElse units <> "0") Then
                ' If this isn't the first set of three non-zero digits that has been processed.
                If (resultString <> "") Then
                    ' Condition ensures that 1001 produces "one thousand and one" rather than "one thousand, one", for example.
                    If (inputString.Length - 1 - i < 3 AndAlso hundreds = "0") Then
                        resultString &= " and "
                    Else
                        ' Comma for readability.
                        resultString &= ", "
                    End If
                End If

                resultString &= convertThreeDigitNumberToWords(hundreds, tens, units)

                resultString &= " " & getMagnitudeName(inputString.Length - i - 3)
            End If
        Next

        If (resultString = " ") Then
            resultString = "zero"
        End If

        Return resultString
    End Function

    Function convertThreeDigitNumberToWords(ByVal hundreds As UInt16, ByVal tens As UInt16, ByVal units As UInt16) As String
        Dim resultString As String = ""

        If (hundreds <> "0") Then
            resultString = getDigitName(hundreds) & " hundred"

            ' If there is a non-zero value for the tens or units, add an "and".
            ' Use to produce 'four hundred and fifty two', rather than 'four hundred fifty two'
            If (tens <> "0" OrElse units <> "0") Then
                resultString &= " and "
            End If
        End If

        If (tens <> "0" OrElse units <> "0") Then
            ' Two digit numbers beginning with 1 are special cases (11 spoken eleven, rather than "ten one", for example).
            ' It won't matter if the number begins with "0" after it has been converted to an integer.
            If (tens = "1" OrElse tens = "0") Then
                resultString &= getDigitName(tens & units)
            Else
                resultString &= getDigitName(tens & "0")

                If (units <> "0") Then
                    resultString &= " " & getDigitName(units)
                End If
            End If
        End If

        Return resultString
    End Function

    Function getMagnitudeName(ByVal digitPlace As UInt16) As String
        ' The power associated with the highest named number I could find with 10^(power of divisible by 3) (a vigintillion)
        Const MAXIMUM_NUMBER_NAME As UInt16 = 63

        Dim resultString As String = ""

        Do Until (digitPlace = 0)
            Dim magnitudeValue As UInt16

            If (digitPlace Mod MAXIMUM_NUMBER_NAME <> 0) Then
                magnitudeValue = digitPlace Mod MAXIMUM_NUMBER_NAME
            Else
                magnitudeValue = MAXIMUM_NUMBER_NAME
            End If

            Select Case magnitudeValue
                Case 3
                    resultString &= "thousand"
                Case 6
                    resultString &= "million"
                Case 9
                    resultString &= "billion"
                Case 12
                    resultString &= "trillion"
                Case 15
                    resultString &= "quadrillion"
                Case 18
                    resultString &= "quintillion"
                Case 21
                    resultString &= "sextillion"
                Case 24
                    resultString &= "septillion"
                Case 27
                    resultString &= "octillion"
                Case 30
                    resultString &= "nonillion"
                Case 33
                    resultString &= "decillion"
                Case 36
                    resultString &= "undecillion"
                Case 39
                    resultString &= "duodecillion"
                Case 42
                    resultString &= "tredecillion"
                Case 45
                    resultString &= "quattuordecillion"
                Case 48
                    resultString &= "quindecillion"
                Case 51
                    resultString &= "sexdecillion"
                Case 54
                    resultString &= "septendecillion"
                Case 57
                    resultString &= "octodecillion"
                Case 60
                    resultString &= "novemdecillion"
                Case 63
                    resultString &= "vigintillion"
            End Select

            digitPlace -= magnitudeValue

            If (digitPlace <> 0) Then
                resultString &= " "
            End If
        Loop

        Return resultString
    End Function

    Function getDigitName(ByVal digit As UInt16) As String
        Select Case digit
            Case 0
                Return "zero"
            Case 1
                Return "one"
            Case 2
                Return "two"
            Case 3
                Return "three"
            Case 4
                Return "four"
            Case 5
                Return "five"
            Case 6
                Return "six"
            Case 7
                Return "seven"
            Case 8
                Return "eight"
            Case 9
                Return "nine"
            Case 10
                Return "ten"
            Case 11
                Return "eleven"
            Case 12
                Return "twelve"
            Case 13
                Return "thirteen"
            Case 14
                Return "fourteen"
            Case 15
                Return "fifteen"
            Case 16
                Return "sixteen"
            Case 17
                Return "seventeen"
            Case 18
                Return "eighteen"
            Case 19
                Return "nineteen"
            Case 20
                Return "twenty"
            Case 30
                Return "thirty"
            Case 40
                Return "forty"
            Case 50
                Return "fifty"
            Case 60
                Return "sixty"
            Case 70
                Return "seventy"
            Case 80
                Return "eighty"
            Case 90
                Return "ninety"
        End Select

        Return ""
    End Function
End Module
\$\endgroup\$
1
\$\begingroup\$

Just a few remarks

Naming

Based on the .NET naming guidelines methods should be named using PascalCase casing. Although VB isn't case sensitive it is a good habit to stick to conventions of the language you are using.

Strings

Because strings are immutable each time you use a line like inputString = "0" & inputstring will result in a new string object. This is quit slow. It is best practice to use a StringBuilder especially if you concat strings in a loop.

Instead of using this Do While loop

Do While (inputString.Length Mod DIGITS_IN_HUNDRED <> 0)
    inputString = "0" & inputString
Loop

you should take advantage of the String.PadLeft() method provided by the NET framework.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.