Jump to content

Recommended Posts

Posted (edited)

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
Posted

Nice!

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

 

  Reveal hidden contents

 

 

AutoIt is simple, subtle, elegant. © AutoIt Team

  • Moderators
Posted

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:

  Reveal hidden contents

 

Posted (edited)

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
Posted

  On 7/15/2010 at 7:37 AM, 'Melba23 said:

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?

  On 7/15/2010 at 8:20 AM, 'JamesBrooks said:

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:

Posted (edited)

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
Posted

  On 7/15/2010 at 7:37 AM, 'Melba23 said:

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

Posted

  On 7/15/2010 at 2:29 PM, 'JamesBrooks said:

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:

  On 7/15/2010 at 3:06 PM, 'Spiff59 said:

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.

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
  • Recently Browsing   0 members

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