# Python: Converting Numbers to Words

One of my first self-imposed projects at my job was to recreate a certain annoying application that was a Frankenstein monster: a Microsoft Access file with a VBA GUI. For the most part, the application didn’t even have a database. Anyway, part of the application allowed the user to type in an amount for a check and the VBA code would magically translate the numbers into the text you would normally write on a check. For example, let’s say I wrote a check for \$1,234.56. It would translate that to “one thousand two hundred thirty four dollars and fifty six cents”. I needed a way to do the same thing in Python!

I spent quite some time trying to come up with the correct word formula to put into Google that would return what I needed. Unfortunately, after much labor and a fair bit of frantic typing, I came up with nothing. I had found something that no one in Pythondom had ever done!!! Or my Google-fu was bad that day, but I want to think it was the former.

The VBA code had a clue that I no longer recall. I think it was the name of the VBA library that did the trick. Anyway, whatever it was led me to the following code (note: the link below no longer works):

```#!/usr/bin/env python
'''Convert number to English words
\$./num2eng.py 1411893848129211
one quadrillion, four hundred and eleven trillion, eight hundred and ninety
three billion, eight hundred and forty eight million, one hundred and twenty
nine thousand, two hundred and eleven
\$

Algorithm from http://mini.net/tcl/591
'''

# modified to exclude the "and" between hundreds and tens - mld

__author__ = 'Miki Tebeka '
__version__ = '\$Revision: 7281 \$'

# \$Source\$

import math

# Tokens from 1000 and up
_PRONOUNCE = [
'vigintillion',
'novemdecillion',
'octodecillion',
'septendecillion',
'sexdecillion',
'quindecillion',
'quattuordecillion',
'tredecillion',
'duodecillion',
'undecillion',
'decillion',
'nonillion',
'octillion',
'septillion',
'sextillion',
'quintillion',
'trillion',
'billion',
'million ',
'thousand ',
''
]

# Tokens up to 90
_SMALL = {
'0' : '',
'1' : 'one',
'2' : 'two',
'3' : 'three',
'4' : 'four',
'5' : 'five',
'6' : 'six',
'7' : 'seven',
'8' : 'eight',
'9' : 'nine',
'10' : 'ten',
'11' : 'eleven',
'12' : 'twelve',
'13' : 'thirteen',
'14' : 'fourteen',
'15' : 'fifteen',
'16' : 'sixteen',
'17' : 'seventeen',
'18' : 'eighteen',
'19' : 'nineteen',
'20' : 'twenty',
'30' : 'thirty',
'40' : 'forty',
'50' : 'fifty',
'60' : 'sixty',
'70' : 'seventy',
'80' : 'eighty',
'90' : 'ninety'
}

def get_num(num):
'''Get token <= 90, return '' if not matched'''
return _SMALL.get(num, '')

def triplets(l):
'''Split list to triplets. Pad last one with '' if needed'''
res = []
for i in range(int(math.ceil(len(l) / 3.0))):
sect = l[i * 3 : (i + 1) * 3]
if len(sect) < 3: # Pad last section
sect += [''] * (3 - len(sect))
res.append(sect)
return res

def norm_num(num):
"""Normelize number (remove 0's prefix). Return number and string"""
n = int(num)
return n, str(n)

def small2eng(num):
'''English representation of a number <= 999'''
n, num = norm_num(num)
hundred = ''
ten = ''
if len(num) == 3: # Got hundreds
hundred = get_num(num[0]) + ' hundred'
num = num[1:]
n, num = norm_num(num)
if (n > 20) and (n != (n / 10 * 10)): # Got ones
tens = get_num(num[0] + '0')
ones = get_num(num[1])
ten = tens + ' ' + ones
else:
ten = get_num(num)
if hundred and ten:
return hundred + ' ' + ten
#return hundred + ' and ' + ten
else: # One of the below is empty
return hundred + ten

#FIXME: Currently num2eng(1012) -> 'one thousand, twelve'
# do we want to add last 'and'?
def num2eng(num):
'''English representation of a number'''
num = str(long(num)) # Convert to string, throw if bad number
if (len(num) / 3 >= len(_PRONOUNCE)): # Sanity check
raise ValueError('Number too big')

if num == '0': # Zero is a special case
return 'zero'

# Create reversed list
x = list(num)
x.reverse()
pron = [] # Result accumolator
ct = len(_PRONOUNCE) - 1 # Current index
for a, b, c in triplets(x): # Work on triplets
p = small2eng(c + b + a)
if p:
pron.append(p + ' ' + _PRONOUNCE[ct])
ct -= 1
# Create result
pron.reverse()
return ', '.join(pron)

if __name__ == '__main__':
from sys import argv, exit
from os.path import basename
if len(argv) < 2:
print 'usage: %s NUMBER[s]' % basename(argv[0])
exit(1)
for n in argv[1:]:
try:
print num2eng(n)
except ValueError, e:
print 'Error: %s' % e
```

I modified the code slightly as noted in the comments in the code to match what the VBA code did. Other than that, it's exactly as I found it. I won't explain this piece as it's kind of fun to figure it out for yourself. Hope you like it!

### 5 thoughts on “Python: Converting Numbers to Words”

1. Posting large code snippets online without clear statement of the license is a useless as creating another fart app for a phone.

If you mean your shared code to be used, declare so by stating licensing terms.