Task

Show how to spell out a number in English.

You can use a preexisting implementation or roll your own, but you should support inputs up to at least one million (or the maximum value of your language's default bounded integer type, if that's less).

Support for inputs other than positive integers (like zero, negative integers, and floating-point numbers) is optional.



Related task

   Spelling of ordinal numbers.


DataSection
  numberNames:
  ;small
  Data.s "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"
  Data.s "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
  ;tens
  Data.s "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
  ;big, non-Chuquet system 
  Data.s "thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion"
  Data.s "septillion", "octillion", "nonillion", "decillion", "undecillion", "duodecillion"
  Data.s "tredecillion"
EndDataSection
 
Procedure.s numberWords(number.s)
  ;handles integers from -1E45 to +1E45
  Static isInitialized = #False
  Static Dim small.s(19)
  Static Dim tens.s(9)
  Static Dim big.s(14)
 
  If Not isInitialized
    Restore numberNames
    For i = 1 To 19
      Read.s small(i)
    Next
    For i = 2 To 9
      Read.s tens(i)
    Next
    For i = 1 To 14
      Read.s big(i)
    Next
    isInitialized = #True
  EndIf 
 
  For i = 1 To Len(number)
    If Not FindString("- 0123456789", Mid(number,i,1), 1)
      number = Left(number, i - 1) ;trim number to the last valid character
      Break ;exit loop
    EndIf 
  Next 
 
  Protected IsNegative = #False
  number = Trim(number)
  If Left(number,1) = "-"
    IsNegative = #True
    number = Trim(Mid(number, 2))
  EndIf 
 
  If CountString(number, "0") = Len(number)
    ProcedureReturn "zero"
  EndIf 
 
  If Len(number) > 45
    ProcedureReturn "Number is too big!"
  EndIf 
 
  Protected num.s = number, output.s, unit, unitOutput.s, working
 
  Repeat
    working = Val(Right(num, 2))
    unitOutput = ""
    Select working
      Case 1 To 19
        unitOutput = small(working)
      Case 20 To 99
        If working % 10
          unitOutput = tens(working / 10) + "-" + small(working % 10)
        Else
          unitOutput = tens(working / 10)
        EndIf 
    EndSelect
 
    working = Val(Right(num, 3)) / 100
    If working
      If unitOutput <> ""
        unitOutput = small(working) + " hundred " + unitOutput
      Else
        unitOutput = small(working) + " hundred"
      EndIf 
    EndIf 
 
    If unitOutput <> "" And unit > 0
      unitOutput + " " + big(unit)
      If output <> ""
        unitOutput + ", "
      EndIf 
    EndIf 
 
    output = unitOutput + output
 
    If Len(num) > 3
      num = Left(num, Len(num) - 3)
      unit + 1
    Else
      Break ;exit loop
    EndIf 
  ForEver
 
  If IsNegative
    output = "negative " + output
  EndIf
 
  ProcedureReturn output
EndProcedure
 
Define n$
If OpenConsole()
  Repeat
    Repeat
      Print("Give me an integer (or q to quit)! ")
      n$ = Input()
    Until n$ <> ""
 
    If Left(Trim(n$),1) = "q"
      Break ;exit loop
    EndIf 
    PrintN(numberWords(n$))
  ForEver 
  CloseConsole()
EndIf
 