As an update (conversion) to my article “Simple Number to Words using a Single Loop String Triplets in JavaScript” in JavaScript, I have converted the code to work as a VBA function using the same method of "Single Loop String Triplets".
The explanation of how it works is detailed in the above reference article with images and examples.
The function is for unsigned integers. But may be called twice for whole and fractional parts after a number split at the decimal point. Also currency and sub-currency words could be added easily if a whole/fractional split is made.
It is not intended for the function to check for bad inputs, negative numbers, decimals, etc. as this could be left to a another higher function that will call this function, therefore the following are not accounted for simplicity:
- No checks for negative numbers.
- No checks for non-number (NaN) strings/data.
- No checks or conversion for exponential notations.
However, large numbers can be passed as a String if necessary.
The “scle” Array may be increased by adding additional scales above “Decillion”.
Examples:
Debug.Print NumToWordsUnsignedInt(777112999)
'Output:
'Seven Hundred Seventy-Seven Million One Hundred Twelve Thousand Nine Hundred Ninety-Nine
Debug.Print NumToWordsUnsignedInt(“222111333444555666777888999111222333”)
'Output:
'Two Hundred Twenty-Two Decillion One Hundred Eleven Nonillion Three Hundred Thirty-Three Octillion Four Hundred Forty-Four Septillion Five Hundred Fifty-Five Sextillion Six Hundred Sixty-Six Quintillion Seven Hundred Seventy-Seven Quadrillion Eight Hundred Eighty-Eight Trillion Nine Hundred Ninety-Nine Billion One Hundred Eleven Million Two Hundred Twenty-Two Thousand Three Hundred Thirty-Three
I would like the code to be reviewed for any bugs, optimization, or improvements. I am sure there is room for improvements and corrections.
Thanks in advance.
Mohsen Alyafei
Function NumToWordsUnsignedInt(ByVal NumIn As String)
'-------------------------------------------------------------
'Convert Unsigned Integer Number to English Words (US System)
'Using a Single Loop String Triplets (SLST) Method
'Mohsen Alyafei 10 July 2019
'Call it separately for a whole number and a fraction
'-------------------------------------------------------------
Dim Ones(), Tens(), Scle(), Sep, NumAll As String, t As String, N1 As Integer, N2 As Integer, Triplet, L, i, j
Ones = Array("", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen")
Tens = Array("", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety")
Scle = Array("", "Thousand", "Million", "Billion", "Trillion", "Quadrillion", "Quintillion", "Sextillion", "Septillion", "Octillion", "Nonillion", "Decillion")
NumIn = String((Len(NumIn) * 2) Mod 3, "0") & NumIn 'Create shortest string Triplets (0 padded)
L = Len(NumIn) / 3 - 1: j = 1 'Get total no. of Triplets and init count into Triplets
For i = L To 0 Step -1 'Loop starting with Most Signifct Triplet (MST)
Triplet = Mid(NumIn, j, 3) 'Get a Triplet starting from LH
If Triplet <> "000" Then 'Skip empty Triplets
Sep = IIf(Right(Triplet, 1) <> "0", "-", "") 'Only if hyphen needed for nums 21 to 99
N1 = Left(Triplet, 1): N2 = Right(Triplet, 2) 'Get Hundreds digit and 2 lowest digits (00 to 99)
'First Spell the 2 lowest digits in N2
If N2 > 19 Then t = Tens(Val(Mid(Triplet, 2, 1))) & Sep & Ones(Val(Right(Triplet, 1))) Else t = Ones(N2)
'Add " hundred" if needed, Create number with scale, and join the Triplet scales to previous
NumAll = NumAll & Trim(IIf(N1 > 0, Ones(N1) & " Hundred", "") & " " & t) & " " & Scle(i) & " "
End If
j = j + 3 'Point to next Triplet position
Next 'Go for next lower Triplets (move to RH)
NumToWordsUnsignedInt = Trim(NumAll) 'Return trimming excess spaces
End Function
EDIT 1: Variables Re-Naming for better readability
Variable names updated based on sugegstions.
'-------------------------------------------------------------
Function NumToWordsUnsignedInt(ByVal NumIn As String)
'-------------------------------------------------------------
'Convert Unsigned Integer Number to English Words (US System)
'Using a Single Loop String Triplets (SLST) Method
'Mohsen Alyafei 12 July 2019
'Call it separately for a whole number and a fraction
'-------------------------------------------------------------
Dim Ones(), tens(), Scle(), Sep, NumAll, W_Tens, Triplet, TotalTriplets, i, TripletPos
Dim N_Hundrds As Integer, N_Tens As Integer
Ones = Array("", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen")
tens = Array("", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety")
Scle = Array("", "Thousand", "Million", "Billion", "Trillion", "Quadrillion", "Quintillion", "Sextillion", "Septillion", "Octillion", "Nonillion", "Decillion")
NumIn = String((Len(NumIn) * 2) Mod 3, "0") & NumIn 'Create shortest string Triplets (0 padded)
TotalTriplets = Len(NumIn) / 3 - 1: TripletPos = 1 'Get total no. of Triplets and init count into Triplets
For i = TotalTriplets To 0 Step -1 'Loop starting with Most Signifct Triplet (MST)
Triplet = Mid(NumIn, TripletPos, 3) 'Get a Triplet starting from LH
If Triplet <> "000" Then 'Skip empty Triplets
Sep = IIf(Right(Triplet, 1) <> "0", "-", "") 'Only if hyphen needed for nums 21 to 99
N_Hundrds = Left(Triplet, 1) 'Get the Hundreds digit
N_Tens = Right(Triplet, 2) 'Get 2 lowest digits (00 to 99)
'First Spell the 2 lowest digits in N_Tens into W_Tens
If N_Tens > 19 Then W_Tens = tens(Val(Mid(Triplet, 2, 1))) & Sep & Ones(Val(Right(Triplet, 1))) Else W_Tens = Ones(N_Tens)
'Add " hundred" if needed, Create number with scale, and join the Triplet scales to previous
NumAll = NumAll & Trim(IIf(N_Hundrds > 0, Ones(N_Hundrds) & " Hundred", "") & " " & W_Tens) & " " & Scle(i) & " "
End If
TripletPos = TripletPos + 3 'Point to next Triplet position
Next 'Go for next lower Triplets (move to RH)
NumToWordsUnsignedInt = Trim(NumAll) 'Return trimming excess spaces
End Function
```
Dim Ones(), Tens(), Scle(), Sep, NumAll As String
produces 3Variant
arrays, oneVariant
and oneString
variable. The code will, of course work withVariants
, but if they're all expected to beString
, then skipping the implicit conversion ofVariant
toString
will improve performance and readability. \$\endgroup\$T
is declaredString
.Triplet, L, i, j
however are allVariant
. I'd strongly recommend some better names for those single letter variables - what isT
, anyway?i
is OK as a loop index (pretty darn conventional), butL
?,j
? those are meaningless.N1
andN2
are pretty... vague as well. \$\endgroup\$