NumToWord

Recommended Posts

After some confusion in general help and support I ended up writing this for absolutely no reason. Never mind, in the toolbox it goes.

• Uses the short scale (used by the USA - billion = thousand million)
• Huge numbers can be converted (up to 134217727 digits before the decimal point has been tested... After the decimal point depends on how much memory you have available)
• Uses number names up to vigintillion
• Negative numbers and decimals allowed.
• Does the 'and' after the big numbers.
• Now includes reversal (NumFromWord)

```Func NumFromWord(\$sWord)
Local \$sDigits = "zero|one|two|three|four|five|six|seven|eight|nine"
Local \$sTeens = "eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen"
Local \$sTens = "ten|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety"
Local \$sBig = "thousand|million|billion|trillion|quadrillion|quintillion|sextillion|septillion|octillion|nonillion|" & _
"decillion|undecillion|duodecillion|tredecillion|quattuordecillion|quindecillion|sexdecillion|septendecillion|octodecillion|novemdecillion|vigintillion"

Local \$asDigits = StringSplit(\$sDigits, "|", 2)
Local \$asTeens = StringSplit(\$sTeens, "|", 2)
Local \$asTens = StringSplit(\$sTens, "|", 2)
Local \$asBig = StringSplit(\$sBig, "|", 2)

Local \$sRet = ""
Local \$iTemp[3] = [0, 0, 0]
Local \$aWords

; check...
If Not StringRegExp(\$sWord, "^(minus\s+)?((((" & \$sTens & ")\s+)?(" & \$sDigits & ")|(" & \$sTeens & ")|(" & \$sTens & "))\s+(hundred|" & \$sBig & ")(\s+and)?(\s+|\z))*(((" & \$sTens & ")\s+)?(" & \$sDigits & ")?|(" & \$sTeens & ")|(" & \$sTens & "))(\s+point(\s+(" & \$sDigits & "))+)?\$") Then _
Return SetError(1)

\$sWord = StringReplace(\$sWord, " and", "")
For \$i = 0 To 8
\$sWord = StringReplace(\$sWord, \$asTeens[\$i], "ten " & \$asDigits[\$i + 1])
Next

\$aWords = StringSplit(\$sWord, " ")

If StringInStr(\$sWord, " point ") Then
Do
For \$n = 0 To UBound(\$asDigits) - 1
If \$asDigits[\$n] = \$aWords[\$aWords[0]] Then
\$sRet = \$n & \$sRet
ExitLoop
EndIf
Next

\$aWords[0] -= 1
Until \$aWords[\$aWords[0]] <> "point"
\$sRet = "." & \$sRet

If \$aWords[\$aWords[0]] = "zero" Then \$sRet = "0" & \$sRet
EndIf

For \$i = \$aWords[0] To 1 Step -1
If \$i = 1 And \$aWords[1] = "minus" Then
\$sRet = "-" & \$sRet
ExitLoop
EndIf

If StringInStr("|" & \$sDigits & "|", "|" & \$aWords[\$i] & "|") Then ; Digit
If \$iTemp[0] <> 0 Then Return SetError(2, \$i, "") ; Invalid word: @extended, expected large identifier not digit.

For \$n = 0 To UBound(\$asDigits) - 1
If \$asDigits[\$n] = \$aWords[\$i] Then
\$iTemp[0] = \$n
ExitLoop
EndIf
Next
ElseIf StringInStr("|" & \$sTens & "|", "|" & \$aWords[\$i] & "|") Then ; Tens
If \$iTemp[1] <> 0 Then Return SetError(3, \$i, "") ; Invalid word: @extended, expected large identifier not ten.

For \$n = 0 To UBound(\$asTens) - 1
If \$asTens[\$n] = \$aWords[\$i] Then
\$iTemp[1] = \$n + 1
ExitLoop
EndIf
Next
ElseIf \$aWords[\$i] = "hundred" Then ; hundred
If \$iTemp[2] <> 0 Then Return SetError(4, \$i, "") ; Invalid word: @extended, expected large identifier not hundred.

For \$n = 0 To UBound(\$asDigits) - 1
If \$asDigits[\$n] = \$aWords[\$i - 1] Then
\$iTemp[2] = \$n
\$i -= 1
ExitLoop
EndIf
Next
ElseIf StringInStr("|" & \$sBig & "|", "|" & \$aWords[\$i] & "|") Then ; BIG
\$sRet = \$iTemp[2] & \$iTemp[1] & \$iTemp[0] & \$sRet

\$iTemp[0] = 0
\$iTemp[1] = 0
\$iTemp[2] = 0
Else
Return SetError(4, \$i, "") ; Invalid word: @extended, word not recognized.
EndIf
Next
\$sRet = \$iTemp[2] & \$iTemp[1] & \$iTemp[0] & \$sRet

While StringLeft(\$sRet, 1) = "0" And StringLeft(\$sRet, 2) <> "0."
\$sRet = StringTrimLeft(\$sRet, 1)
WEnd

; No trailing 0's if decimal
If StringInStr(\$sRet, ".") Then
While StringRight(\$sRet, 1) = "0"
\$sRet = StringTrimRight(\$sRet, 1)
WEnd
If StringRight(\$sRet, 1) = "." Then \$sRet = StringTrimRight(\$sRet, 1)
EndIf

Return \$sRet
EndFunc   ;==>NumFromWord

Func NumToWord(\$iNum)
\$iNum = String(\$iNum)
\$iNum = StringStripWS(\$iNum, 8)
If Not StringRegExp(\$iNum, "^-?\d+?(\.\d+)?\$") Then Return SetError(2, 0, "")
If \$iNum = "0" Then Return "zero"

Local \$asDigits[10] = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
Local \$asTeens[9] = ["eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]
Local \$asTens[9] = ["ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]
Local \$asBig[21] = ["thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion", "septillion", "octillion", "nonillion", _
"decillion", "undecillion", "duodecillion", "tredecillion", "quattuordecillion", "quindecillion", "sexdecillion", "septendecillion", "octodecillion", "novemdecillion", "vigintillion"]

While StringLeft(\$iNum, 1) = "0" And StringLeft(\$iNum, 2) <> "0."
\$iNum = StringTrimLeft(\$iNum, 1)
WEnd

; No trailing 0's if decimal
If StringInStr(\$iNum, ".") Then
While StringRight(\$iNum, 1) = "0"
\$iNum = StringTrimRight(\$iNum, 1)
WEnd
If StringRight(\$iNum, 1) = "." Then \$iNum = StringTrimRight(\$iNum, 1)
EndIf

Local \$iTemp
Local \$iLen
Local \$sRet = ""
Local \$nDec = ""

; Do decimal places later
If StringInStr(\$iNum, ".") Then
\$nDec = StringMid(\$iNum, StringInStr(\$iNum, ".") + 1)
\$iNum = StringLeft(\$iNum, StringInStr(\$iNum, ".") - 1)
EndIf

; Check negative
If StringLeft(\$iNum, 1) = "-" Then
\$iNum = StringTrimLeft(\$iNum, 1)
\$sRet &= "minus "
EndIf

; Very big numbers
For \$i = 21 To 1 Step -1
If StringLen(\$iNum) > \$i * 3 Then
\$iLen = Mod(StringLen(\$iNum), 3)
If \$iLen = 0 Then \$iLen = 3

\$iTemp = StringLeft(\$iNum, \$iLen)
\$iNum = StringTrimLeft(\$iNum, \$iLen)

\$iTemp = NumToWord(\$iTemp)
If \$iTemp = "" Or \$iTemp = "0" Then ContinueLoop
If Not @error Then \$sRet &= \$iTemp & " " & \$asBig[\$i - 1] & " "
EndIf
Next

; hundreds
If StringLen(\$iNum) >= 3 Then
\$iTemp = StringLeft(\$iNum, 1)
\$iNum = StringTrimLeft(\$iNum, 1)

If \$iTemp <> "0" Then \$sRet &= \$asDigits[Int(\$iTemp)] & " hundred "
If \$iNum <> "00" Then \$sRet &= "and "
EndIf

If StringLen(\$iNum) = 2 And StringLeft(\$iNum, 1) = "1" And StringRight(\$iNum, 1) <> "0" Then
\$iTemp = StringRight(\$iNum, 1)
\$sRet &= \$asTeens[Int(\$iTemp) - 1] & " "

\$iNum = ""
Else
; Tens
If StringLen(\$iNum) = 2 Then
\$iTemp = StringLeft(\$iNum, 1)
\$iNum = StringTrimLeft(\$iNum, 1)

If \$iTemp <> "0" Then \$sRet &= \$asTens[Int(\$iTemp) - 1] & " "
EndIf

; Digits
If StringLen(\$iNum) = 1 Then
If \$iNum <> "0" Then \$sRet &= \$asDigits[Int(\$iNum)] & " "

\$iNum = ""
EndIf
EndIf

If \$nDec <> "" Then
If \$sRet = "" Or \$sRet = "minus " Then \$sRet &= "zero "
\$sRet &= "point "

Do
\$iTemp = StringLeft(\$nDec, 1)
\$nDec = StringTrimLeft(\$nDec, 1)

\$sRet &= \$asDigits[Int(\$iTemp)] & " "
Until \$nDec = ""
EndIf

\$sRet = StringTrimRight(\$sRet, 1)

Return \$sRet
EndFunc   ;==>NumToWord```

Mat

Edited by Mat

Share on other sites

Nice!

What about make it work in reverse mode, i mean convert Words to numbers?

Spoiler

Using OS: Win 7 Professional, Using AutoIt Ver(s): 3.3.6.1 / 3.3.8.1

My Work...

Spoiler

Like the Projects/UDFs/Examples? Please rate the topic (up-right corner of the post header: Rating )

* === My topics === *

==================================================

==================================================

AutoIt is simple, subtle, elegant. © AutoIt Team

Share on other sites

Nice!

What about make it work in reverse mode, i mean convert Words to numbers?

I knew someone would ask that

I'm working on it.

Share on other sites

Mat,

It took a bit of finding, but you might find some of the posts in this thread of interest.

M23

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

Share on other sites

Mat,

Well done for solving Problem 17!

```Global \$strWord
Global \$strFin
For \$i = 1 To 1000
\$strWord = StringStripWS(NumToWord(\$i), 8)
ConsoleWrite(\$strWord & @CRLF)
\$strFin += StringLen(\$strWord)
Next

ConsoleWrite(\$strFin & @CRLF)

Func NumToWord(\$iNum)
\$iNum = String(\$iNum)
\$iNum = StringStripWS(\$iNum, 8)
If Not StringRegExp(\$iNum, "^-?\d+?(\.\d+)?\$") Then Return SetError(2, 0, "")
If \$iNum = "0" Then Return "zero"

Local \$asDigits[10] = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
Local \$asTeens[9] = ["eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]
Local \$asTens[9] = ["ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]
Local \$asBig[21] = ["thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion", "septillion", "octillion", "nonillion", _
"decillion", "undecillion", "duodecillion", "tredecillion", "quattuordecillion", "quindecillion", "sexdecillion", "septendecillion", "octodecillion", "novemdecillion", "vigintillion"]

While StringLeft(\$iNum, 1) = "0" And StringLeft(\$iNum, 2) <> "0."
\$iNum = StringTrimLeft(\$iNum, 1)
WEnd

; No trailing 0's if decimal
If StringInStr(\$iNum, ".") Then
While StringRight(\$iNum, 1) = "0"
\$iNum = StringTrimRight(\$iNum, 1)
WEnd
If StringRight(\$iNum, 1) = "." Then \$iNum = StringTrimRight(\$iNum, 1)
EndIf

Local \$iTemp
Local \$iLen
Local \$sRet = ""
Local \$nDec = ""

; Do decimal places later
If StringInStr(\$iNum, ".") Then
\$nDec = StringMid(\$iNum, StringInStr(\$iNum, ".") + 1)
\$iNum = StringLeft(\$iNum, StringInStr(\$iNum, ".") - 1)
EndIf

; Check negative
If StringLeft(\$iNum, 1) = "-" Then
\$iNum = StringTrimLeft(\$iNum, 1)
\$sRet &= "minus "
EndIf

; Very big numbers
For \$i = 21 To 1 Step -1
If StringLen(\$iNum) > \$i * 3 Then
\$iLen = Mod(StringLen(\$iNum), 3)
If \$iLen = 0 Then \$iLen = 3

\$iTemp = StringLeft(\$iNum, \$iLen)
\$iNum = StringTrimLeft(\$iNum, \$iLen)

\$iTemp = NumToWord(\$iTemp)
If \$iTemp = "" Or \$iTemp = "0" Then ContinueLoop
If Not @error Then \$sRet &= \$iTemp & " " & \$asBig[\$i - 1] & " "
EndIf
Next

; hundreds
If StringLen(\$iNum) >= 3 Then
\$iTemp = StringLeft(\$iNum, 1)
\$iNum = StringTrimLeft(\$iNum, 1)

If \$iTemp <> "0" Then \$sRet &= \$asDigits[Int(\$iTemp)] & " hundred "
If \$iNum <> "00" Then \$sRet &= "and "
EndIf

If StringLen(\$iNum) = 2 And StringLeft(\$iNum, 1) = "1" And StringRight(\$iNum, 1) <> "0" Then
\$iTemp = StringRight(\$iNum, 1)
\$sRet &= \$asTeens[Int(\$iTemp) - 1] & " "

\$iNum = ""
Else
; Tens
If StringLen(\$iNum) = 2 Then
\$iTemp = StringLeft(\$iNum, 1)
\$iNum = StringTrimLeft(\$iNum, 1)

If \$iTemp <> "0" Then \$sRet &= \$asTens[Int(\$iTemp) - 1] & " "
EndIf

; Digits
If StringLen(\$iNum) = 1 Then
If \$iNum <> "0" Then \$sRet &= \$asDigits[Int(\$iNum)] & " "

\$iNum = ""
EndIf
EndIf

If \$nDec <> "" Then
\$sRet &= "point "

Do
\$iTemp = StringLeft(\$nDec, 1)
\$nDec = StringTrimLeft(\$nDec, 1)

\$sRet &= \$asDigits[Int(\$iTemp)] & " "
Until \$nDec = ""
EndIf

\$sRet = StringTrimRight(\$sRet, 1)

Return \$sRetEndFunc   ;==>NumToWord```

James

P.S, you could cut out a load of unnecessary code for dealing with bigger numbers \$asBig so that it runs even quicker, seeing as the requirement for 17 is 1000.

Edited by JamesBrooks

Share on other sites

It took a bit of finding, but you might find some of the posts in this thread of interest.

That's going to be a lot of work as well... How am I going to translate all the words?

Well done for solving Problem 17!

James

P.S, you could cut out a load of unnecessary code for dealing with bigger numbers \$asBig so that it runs even quicker, seeing as the requirement for 17 is 1000.

I like the big numbers, up to 1000 is easy anyway. I'm working on even bigger numbers (googol etc.).

As for the reversal, here's how I think I can make it work... I can check using this pattern:

`StringRegExp(\$sWord, "^(minus\s+)?((((" & \$sTens & ")\s+)?(" & \$sDigits & ")|(" & \$sTeens & ")|(" & \$sTens & "))\s+(hundred|" & \$sBig & ")(\s+and)?(\s+|\z))*(((" & \$sTens & ")\s+)?(" & \$sDigits & ")?|(" & \$sTeens & ")|(" & \$sTens & "))(\s+point(\s+(" & \$sDigits & "))+)?\$")`

It's then going to go through each word in reverse, storing it in a temporary var, until it reaches a big number. Then it will pad the var to 3 characters and add it to the return string. It should work

Share on other sites

How about number to word to syllables to rhythm?

```>             >                    >
1117   One thou-sand one hun-dered and se-ven-teen
Count  1   2    3    1   2   3     1   2  &   3```

Perhaps you could add that one to Project Euler.

I'm just joking, but I have a similar conversion problem. From musical chord name to formula. The biggest problem here is that chord nomenclature does not follow consistant rules. This is because chord construction is based on a set of instructions which often lead to erroneous interpretation. Different, and frequently incompatible, systems are used in different styles of music. That makes things quite complicated. Anyway, I'm pretty sure I've solved it by now (at least in theory). It's slightly annoying, though no big deal.

Edited by czardas

Share on other sites

P.S we need to edit the old solutions since OnAutoItExit no longer exists.

James

Edited by JamesBrooks

Share on other sites

Mat,

It took a bit of finding, but you might find some of the posts in this thread of interest.

M23

I knew we'd played with this exact same function over a year ago, and that that thread was out there somewhere, but my searches came up empty! Somehow "locale" as a search string never entered my mind. Good job finding it, M23.

I, of course, like the 33-line submission in post #32 of the thread

Share on other sites

P.S we need to edit the old solutions since OnAutoItExit no longer exists.

It's been a long time since I even visited that page... Is there not a system for multiple solutions? I thought I built that in. A better solution would be to rewrite the function for speed

I knew we'd played with this exact same function over a year ago, and that that thread was out there somewhere, but my searches came up empty! Somehow "locale" as a search string never entered my mind. Good job finding it, M23.

I, of course, like the 33-line submission in post #32 of the thread

Thats a nice way to do it... not sure if Megakazillion is a dictionary term though

The post below yours puts me off doing locales though.

Share on other sites

NumFromWord now works There was also a bug with 0.* which missed out the zero.

Share on other sites

I was struggling with WordToNum until I added:

`\$sWord = StringLower(\$sWord)`

Cool stuff, Mat.

Share on other sites

Ah... I will fix it in 6 weeks time