Convert Numerical Currency Into Words

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
AlFlo
Posts: 339
Joined: 29 Nov 2021, 21:46

Convert Numerical Currency Into Words

Post by AlFlo » 04 Feb 2023, 12:54

I frequently have to convert numerical dollar figures like $1,876,543.21 into words, i.e.:

One Million Eight Hundred and Seventy-Six Thousand Five Hundred and Forty-Three Dollars and Twenty-One Cents

And vice-versa.

I've gotten pretty close tinkiering with the following AHK script originally based on a script written by jeeswg (although it writes "and Two One Cents" instead of "and Twenty-One Cents"). But how do I write a script going the OTHER way ... where it converts words into numbers? I.e. how do I place "One Million Eight Hundred and Seventy-Six Thousand Five Hundred and Forty-Three Dollars and Twenty-One Cents" in clipboard and have it spit out "$1,876,543.21"?

Code: Select all

vNum := Clipboard ; I START BY PLACING THE NUMBERS IN CLIPBOARD

tempVar := % JEE_NumToWord(vNum)
 
q:: Send %tempVar%  ; WHEN I PRESS "Q", IT PASTES THE WORD VERSION OF THE NUMBERS
return

;==================================================

;JEE_NumSpell
;JEE_SpellNum
JEE_NumToWord(vNum, vDoTrimFrac:=1, vAddExtraAnd:=0)
{
	if !vNum
		return "zero"

	oArrayX := StrSplit("Thousand,Million,Billion,Trillion,Quadrillion,Quintillion,Sextillion,Septillion,Octillion,Nonillion,Decillion", ",")
	(oArrayU := StrSplit("Zero,One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen,Fourteen,Fifteen,Sixteen,Seventeen,Eighteen,Nineteen", ",")).RemoveAt(0)
	oArrayT := StrSplit("Ten,Twenty,Thirty,Forty,Fifty,Sixty,Seventy,Eighty,Ninety", ",")

	vNum := StrReplace(vNum, "-", "", vCount)
	if vCount
		vOutput := "minus "
	vNum := StrReplace(vNum, ",")
	vNum1 := LTrim(StrSplit(vNum, ".")[1], "0")
	if vDoTrimFrac
		vNum2 := RTrim(StrSplit(vNum, ".")[2], "0")
	else
		vNum2 := StrSplit(vNum, ".")[2]

	if ((vLen := StrLen(vNum1)) > 36)
		return
	if (Mod(vLen, 3) = 1)
		vNum1 := "00" vNum1
	else if (Mod(vLen, 3) = 2)
		vNum1 := "0" vNum1

	vMag := Ceil(vLen/3)-1
	Loop, % Ceil(vLen/3)
	{
		oTemp := StrSplit(SubStr(vNum1, (A_Index*3)-2, 3))
		vTemp := (oTemp.2 * 10) + oTemp.3
		if oTemp.1
			vOutput .= oArrayU[oTemp.1] " Hundred" (vTemp?" and ":" ")
		else if !vMag && (vLen > 3) && vAddExtraAnd
			vOutput .= "and "

		if (vTemp >= 20)
			vOutput .= oArrayT[oTemp.2] (oTemp.3?"-" oArrayU[oTemp.3]:" ")
		else if vTemp
			vOutput .= oArrayU[vTemp]
		vOutput .= " " oArrayX[vMag] " "
		vMag--
	}
vOutput .= "Dollars "
	vOutput := RTrim(vOutput, " ")
	if !vNum2
		return vOutput
	if (vOutput = "")
		vOutput := "zero"
	vOutput .= " and"
	Loop, Parse, vNum2
		; vOutput .= "-" oArrayU[A_LoopField]
	vOutput .= " " oArrayU[A_LoopField]
vOutput .= " Cents "
	return RTrim(vOutput)
}

;==================================================


AlFlo
Posts: 339
Joined: 29 Nov 2021, 21:46

Re: Convert Numerical Currency Into Words

Post by AlFlo » 04 Feb 2023, 19:53

You know, the funny thing is I found something better than my script to convert numbers into words, a website: https://www.calculatorsoup.com/calculators/conversions/numberstowords.php . I wrote a little AHK script to take the number in my clipboard, paste it into that webpages input box, and convert it into words on that website using the correct formatting (title case, currency).

I might be able to find something similar for converting words into numbers.

If not, I might try brute forcing it through Regex, i.e.:

replace "One" every time it appears with "1"
replace "Two" every time it appears with "2"
etc.

The hard parts would probably be:

(1) distinguishing between the words "Twenty Seven" meaning "27" versus "20 7" ... although, for my purposes, I think it would always mean "27"; and

(2) Replacing the last two words with a decimal point and number of cents if the word "cents" appears.

AlFlo
Posts: 339
Joined: 29 Nov 2021, 21:46

Re: Convert Numerical Currency Into Words

Post by AlFlo » 04 Feb 2023, 20:43

Yeah, this website converts words into numbers: https://www.dcode.fr/writing-words-numbers

It would be nice to have an AHK script to be able to convert words and numbers back and forth ...

But - given the existence of websites like these - it's not a necessity.

User avatar
mikeyww
Posts: 26601
Joined: 09 Sep 2014, 18:38

Re: Convert Numerical Currency Into Words

Post by mikeyww » 04 Feb 2023, 21:46

viewtopic.php?p=422642#p422642 (would need to revise for large numbers)

Just proof of concept, may need debugging:

Code: Select all

; This script converts words to a number
#Requires AutoHotkey v2.0
MsgBox wordsToNum("One Million Eight Hundred and Seventy-Six Thousand Five Hundred and Forty-Three")

wordsToNum(str) {
 Static tens     := Map('ten'  , 10, 'twenty' , 20, 'thirty', 30, 'forty' , 40, 'fifty', 50
                      , 'sixty', 60, 'seventy', 70, 'eighty', 80, 'ninety', 90)
      , ones     := Map('one', 1, 'two'  , 2, 'three', 3, 'four', 4, 'five', 5
                      , 'six', 6, 'seven', 7, 'eight', 8, 'nine', 9)
      , place    := Map('million', 1000000, 'thousand', 1000, 'hundred', 100)
      , tensStr  := "", onesStr := ""
 If !tensStr
  For word in tens
   tensStr .= (tensStr = "" ? "" : "|") word
 If !onesStr
  For word in ones
   onesStr .= (onesStr = "" ? "" : "|") word
 str := StrReplace(Format("{:L}", str), " and ", " ")
 For each, pl in ['million', 'thousand', 'hundred'] {
  RegExMatch(str, 'i)((.+)\h+' pl ')?(\h*(.*))?', &m)
  If m[2]
   Return (wordsToNum(m[2]) * place[pl] || 0) + (wordsToNum(m[4]) || 0)
 }
 RegExMatch(m[4], 'i)(' tensStr ')?[- ]?(' onesStr ')?', &m)
 Return (tens.Has(m[1]) ? tens[m[1]] : 0) + (ones.Has(m[2]) ? ones[m[2]] : 0)
}
image230204-2145-001.png
Output
image230204-2145-001.png (3.38 KiB) Viewed 1126 times
Last edited by mikeyww on 04 Feb 2023, 22:20, edited 3 times in total.

gmoises
Posts: 74
Joined: 18 Nov 2017, 16:43

Re: Convert Numerical Currency Into Words

Post by gmoises » 04 Feb 2023, 22:15

Corrected a bug in the get cents part

Added Format option:
If Format is "$" or If The first character in Num is "$"
the return includes "Dollars"

Num can include commas (,)

Code: Select all

N := "$123456789.045"
MsgBox % SpellNumber(N)
ExitApp

SpellNumber(Num, Format := "") {
	Place := [" ", " Thousand ", " Million ", " Billion ", " Trillion "]
	Num := Trim(StrReplace(Num, ",", ""))
	If SubStr(Num, 1, 1) = "$" or InStr(Format, "$")
		Dls := "Dollars ", Num := StrReplace(Num, "$", "")
	
	; get cents
	If DecimalPoint := InStr(Num, ".")
	{	Cen := Tens(SubStr(SubStr(Num, DecimalPoint + 1) . "00", 1, 2))
		Num := SubStr(Num, 1, DecimalPoint - 1)
	}

	Cnt := 1
	While Num != ""
	{	h := Hundreds(SubStr(Num, -2))
		If h
			Dollars := h . Place[Cnt] . Dollars
		Num := StrLen(Num) > 3 ? SubStr(Num, 1, -3) : ""
		++Cnt
	}
	
	Switch Cen
	{	Case "":
			Cents := Dls . " and No Cents"
		Case "One":
			Cents := Dls . " and One Cent"
		Default:
			Cents := Dls . " and " . Cen . " Cents"
	}
	Return, StrReplace(Dollars . Cents, "  ", " ")
}

; get hundreds
Hundreds(Num) {
	If (Num = 0)
		Return, ""
	Num := SubStr("000" . Num, -2)
	If SubStr(Num, 1, 1) != "0"
		Ret := Digits(SubStr(Num, 1, 1)) . " Hundred "
	If SubStr(Num, 2, 1) != "0"
		Return, Ret . Tens(SubStr(Num, 2))
	Else
		Return, Ret . Digits(SubStr(Num, 3))
}

; get tens
Tens(Num) {
	If (Num > 9 and Num < 20)
		Switch Num
		{	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"
		}
	Else
		Switch SubStr(Num, 1, 1)
		{	Case 2: Ret := "Twenty "
			Case 3: Ret := "Thirty "
			Case 4: Ret := "Forty "
			Case 5: Ret := "Fifty "
			Case 6: Ret := "Sixty "
			Case 7: Ret := "Seventy "
			Case 8: Ret := "Eighty "
			Case 9: Ret := "Ninety "
		}
	Return, Ret . Digits(SubStr(Num, 2, 1))
}

Digits(Num) {
	Switch Num
	{	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"
		Default: Return, ""
	}	
}
Last edited by gmoises on 05 Feb 2023, 17:22, edited 4 times in total.

AlFlo
Posts: 339
Joined: 29 Nov 2021, 21:46

Re: Convert Numerical Currency Into Words

Post by AlFlo » 05 Feb 2023, 00:50

mikeyww, thank you ... my apologies but I haven't made the move to AHK 2.0 yet.

AlFlo
Posts: 339
Joined: 29 Nov 2021, 21:46

Re: Convert Numerical Currency Into Words

Post by AlFlo » 05 Feb 2023, 00:53

gmoises, looks very promising. But no matter what number I test, it says "no cents".

User avatar
mikeyww
Posts: 26601
Joined: 09 Sep 2014, 18:38

Re: Convert Numerical Currency Into Words

Post by mikeyww » 05 Feb 2023, 07:56

A v1 version is below. One could then parse the string for dollars and cents, generate the two function calls, and add the numbers.

Code: Select all

; This script converts words to a number
#Requires AutoHotkey v1.1.33
MsgBox % wordsToNum("One Million Eight Hundred and Seventy-Six Thousand Five Hundred and Forty-Three")

wordsToNum(str) {
 Static tens     := {"ten"  : 10, "twenty" : 20, "thirty": 30, "forty" : 40, "fifty": 50
                   , "sixty": 60, "seventy": 70, "eighty": 80, "ninety": 90}
      , ones     := {"one": 1, "two"  : 2, "three": 3, "four": 4, "five": 5
                   , "six": 6, "seven": 7, "eight": 8, "nine": 9}
      , place    := {"million": 1000000, "thousand": 1000, "hundred": 100}
      , tensStr  := "", onesStr := ""
 If !tensStr
  For word in tens
   tensStr .= (tensStr = "" ? "" : "|") word
 If !onesStr
  For word in ones
   onesStr .= (onesStr = "" ? "" : "|") word
 str := StrReplace(Format("{:L}", str), " and ", " ")
 For each, pl in ["million", "thousand", "hundred"] {
  RegExMatch(str, "i)((.+)\h+" pl ")?(\h*(.*))?", m)
  If m2
   Return ((n1 := wordsToNum(m2)) ? n1 * place[pl] : 0) + ((n2 := wordsToNum(m4)) ? n2 : 0)
 }
 RegExMatch(m4, "i)(" tensStr ")?[- ]?(" onesStr ")?", m)
 Return (tens.HasKey(m1) ? tens[m1] : 0) + (ones.HasKey(m2) ? ones[m2] : 0)
}

AlFlo
Posts: 339
Joined: 29 Nov 2021, 21:46

Re: Convert Numerical Currency Into Words

Post by AlFlo » 05 Feb 2023, 13:14

Wow, thank you mikeyww!!!

User avatar
mikeyww
Posts: 26601
Joined: 09 Sep 2014, 18:38

Re: Convert Numerical Currency Into Words

Post by mikeyww » 05 Feb 2023, 13:40

Numbers between 11 and 19 might need some additions here!


User avatar
mikeyww
Posts: 26601
Joined: 09 Sep 2014, 18:38

Re: Convert Numerical Currency Into Words

Post by mikeyww » 05 Feb 2023, 14:07

Number to words is fairly straightforward. Words to number gets more challenging!

AlFlo
Posts: 339
Joined: 29 Nov 2021, 21:46

Re: Convert Numerical Currency Into Words

Post by AlFlo » 05 Feb 2023, 16:23

gmoises wrote:
04 Feb 2023, 22:15
Corrected a bug in thr get cents part

Code: Select all

N := "123456789.999"
MsgBox % SpellNumber(N)

SpellNumber(Num) {
	Place := [" ", " Thousand ", " Million ", " Billion ", " Trillion "]
	Num := Trim(Num)
	
	; get cents
	If DecimalPoint := InStr(Num, ".")
	{	Cen := Tens(SubStr("00" . SubStr(Num, DecimalPoint + 1), -1))
		Num := SubStr(Num, 1, DecimalPoint - 1)
	}
	
	Cnt := 1
	While Num != ""
	{	h := Hundreds(SubStr(Num, -2))
		If h
			Dollars := h . Place[Cnt] . Dollars
		Num := StrLen(Num) > 3 ? SubStr(Num, 1, -3) : ""
		++Cnt
	}
	
	Switch Cen
	{	Case "":
			Cents := " and No Cents"
		Case "One":
			Cents := " and One Cent"
		Default:
			Cents := " and " . Cen . " Cents"
	}
	Return, StrReplace(Dollars . Cents, "  ", " ")
}

; get hundreds
Hundreds(Num) {
	If (Num = 0)
		Return, ""
	Num := SubStr("000" . Num, -2)
	If SubStr(Num, 1, 1) != "0"
		Ret := Digits(SubStr(Num, 1, 1)) . " Hundred "
	If SubStr(Num, 2, 1) != "0"
		Return, Ret . Tens(SubStr(Num, 2))
	Else
		Return, Ret . Digits(SubStr(Num, 3))
}

; get tens
Tens(Num) {
	If (Num > 9 and Num < 20)
		Switch Num
		{	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"
		}
	Else
		Switch SubStr(Num, 1, 1)
		{	Case 2: Ret := "Twenty "
			Case 3: Ret := "Thirty "
			Case 4: Ret := "Forty "
			Case 5: Ret := "Fifty "
			Case 6: Ret := "Sixty "
			Case 7: Ret := "Seventy "
			Case 8: Ret := "Eighty "
			Case 9: Ret := "Ninety "
		}
	Return, Ret . Digits(SubStr(Num, 2, 1))
}

Digits(Num) {
	Switch Num
	{	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"
		Default: Return, ""
	}	
}
gmoises, your script is getting close and this is getting exciting! You did fix the cents, but there is something off with the dollar calculations. For example, when I have N := "7,123,456,789.55", I get the following result:
Test Result.png
Test Result.png (2.84 KiB) Viewed 831 times
In other words, it shows "Seven Trillion Hundred Twelve Billion Three Hundred Four Million Five Hundred Sixty Thousand Seven Hundred Eighty Nine and Fifty Five Cents " when it should be "Seven Billion One Hundred Twenty-Three Million Four Hundred Fifty-Six Thousand Seven Hundred Eighty-Nine Dollars and Fifty-Five Cents.

AlFlo
Posts: 339
Joined: 29 Nov 2021, 21:46

Re: Convert Numerical Currency Into Words

Post by AlFlo » 05 Feb 2023, 18:45

The AHK script works perfectly on many numbers, but fails on some large numbers.

AlFlo
Posts: 339
Joined: 29 Nov 2021, 21:46

Re: Convert Numerical Currency Into Words

Post by AlFlo » 06 Feb 2023, 01:42

gmoises,

Your revised script seems to work really well. Thanks!!!

Post Reply

Return to “Ask for Help (v1)”