Jump to content

NumToWord


Mat
 Share

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

    ; No leading 0's
    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"]


    ; No leading 0's
    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
Link to comment
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

AutoIt_Rus_Community.png AutoIt Russian Community

My Work...

Spoiler

AutoIt_Icon_small.pngProjects: ATT - Application Translate Tool {new}| BlockIt - Block files & folders {new}| SIP - Selected Image Preview {new}| SISCABMAN - SciTE Abbreviations Manager {new}| AutoIt Path Switcher | AutoIt Menu for Opera! | YouTube Download Center! | Desktop Icons Restorator | Math Tasks | KeyBoard & Mouse Cleaner | CaptureIt - Capture Images Utility | CheckFileSize Program

AutoIt_Icon_small.pngUDFs: OnAutoItErrorRegister - Handle AutoIt critical errors {new}| AutoIt Syntax Highlight {new}| Opera Library! | Winamp Library | GetFolderToMenu | Custom_InputBox()! | _FileRun UDF | _CheckInput() UDF | _GUIInputSetOnlyNumbers() UDF | _FileGetValidName() UDF | _GUICtrlCreateRadioCBox UDF | _GuiCreateGrid() | _PathSplitByRegExp() | _GUICtrlListView_MoveItems - UDF | GUICtrlSetOnHover_UDF! | _ControlTab UDF! | _MouseSetOnEvent() UDF! | _ProcessListEx - UDF | GUICtrl_SetResizing - UDF! | Mod. for _IniString UDFs | _StringStripChars UDF | _ColorIsDarkShade UDF | _ColorConvertValue UDF | _GUICtrlTab_CoverBackground | CUI_App_UDF | _IncludeScripts UDF | _AutoIt3ExecuteCode | _DragList UDF | Mod. for _ListView_Progress | _ListView_SysLink | _GenerateRandomNumbers | _BlockInputEx | _IsPressedEx | OnAutoItExit Handler | _GUICtrlCreateTFLabel UDF | WinControlSetEvent UDF | Mod. for _DirGetSizeEx UDF
 
AutoIt_Icon_small.pngExamples: 
ScreenSaver Demo - Matrix included | Gui Drag Without pause the script | _WinAttach()! | Turn Off/On Monitor | ComboBox Handler Example | Mod. for "Thinking Box" | Cool "About" Box | TasksBar Imitation Demo

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

* === My topics === *

==================================================
My_Userbar.gif
==================================================

 

 

 

AutoIt is simple, subtle, elegant. © AutoIt Team

Link to comment
Share on other sites

  • Moderators

Mat,

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

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png 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

 

Link to comment
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"]

    ; No leading 0's
    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
Link to comment
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 :blink:

Link to comment
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. :blink:

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
Link to comment
Share on other sites

Mat,

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

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 ;)

Link to comment
Share on other sites

Uploaded solution to Google Code :P

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 :blink:

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.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...