Numbers to English Strings in Python3 Inspired by the recent surge of questions dealing with changing numbers to their English equivalents (See here, here, and here), I decided to write my own version in Python.

zero_to_nineteen = (
    "zero",
    "one",
    "two",
    "three",
    "four",
    "five",
    "six",
    "seven",
    "eight",
    "nine",
    "ten",
    "eleven",
    "twelve",
    "thirteen",
    "fourteen" ,
    "fifteen",
    "sixteen",
    "seventeen",
    "eighteen",
    "nineteen"
)

tens = (
    "zero",
    "ten",
    "twenty",
    "thirty",
    "forty",
    "fifty",
    "sixty",
    "seventy",
    "eighty",
    "ninety"
)

suffixes = {
    3 : "thousand",
    6 : "million",
    9 : "billion",
    12 : "trillion",
    15 : "quadrillion",
    18 : "quintillion",
    21 : "sextillion",
    24 : "septillion",
    27 : "octillion",
    30 : "nonillion",
    33 : "decillion",
    36 : "undecillion"
    39 : "duodecillion",
    42 : "tredicillion",
    45 : "quattuordecillion",
    48 : "quinquadecillion",
    51 : "sedecillion",
    54 : "septendecillion",
    57 : "octodecillion",
    60 : "novendecillion",
    63 : "vigintillion",
    66 : "unvigintillion",
    69 : "duovigintillion",
    72 : "tresvigintillion",
    75 : "quattuorvigintillion",
    78 : "quinquavigintillion",
    81 : "sesvigintillion",
    84 : "septemvigintillion",
    87 : "octovigintillion",
    90 : "novemvigintillion",
    93 : "trigintillion",
    96 : "untrigintillion",
    99 : "duotrigintillion",
    102 : "trestrigintilion",
    105 : "quattuortrigintillion",
    108 : "quinquatrigintillion",
    111 : "sestrigintillion",
    114 : "septentrigintillion",
    117 : "octotrigintillion",
    120 : "noventrigintillion",
    123 : "quadragintillion"
}

def spell_out(number):
    """Returns a string representation of the number, in english"""
    if isinstance(number, float):
        raise ValueError("number must be an integer")

    if number &lt; 0:
        return "negative " + spell_out(-1 * number)

    if number &lt; 20:
        return zero_to_nineteen[number]

    if number &lt; 100:
        tens_digit = number // 10
        ones_digit = number % 10
        if ones_digit == 0:
            return tens[tens_digit]
        else:
            return "{}-{}".format(tens[tens_digit], zero_to_nineteen[ones_digit])

    if number &lt; 1000:
        hundreds_digit = zero_to_nineteen[number // 100]
        rest = number % 100
        if rest == 0:
            return "{} hundred".format(hundreds_digit)
        else:
            return "{} hundred {}".format(hundreds_digit, spell_out(rest))

    suffix_index = log(number)
    suffix_index -= suffix_index % 3

    prefix = spell_out(number // 10 ** suffix_index)
    suffix = suffixes[suffix_index]

    rest_of_number = number % (10 ** suffix_index)

    if suffix_index in suffixes:
        if number % (10 ** suffix_index) == 0:
            return "{} {}".format(prefix, suffix)
        else:
            return "{} {} {}".format(prefix, suffix, spell_out(rest_of_number))

    return "infinity"

def log(number):
    """Returns integer which is log base 10 of number"""
    answer = 0
    while number &gt; 9:
        answer += 1
        number //= 10
    return answer


I would appreciate any comments on my code.
It looks mostly fine. I only have three comments:


The definitions of zero_to_nineteen and tens  is too long for my taste. It will look more readable like this::

tens = ("zero", "ten", "twenty", "thirty", "forty",
        "fifty", "sixty", "seventy", "eighty", "ninety")

Reject floats straight away is too much. One cannot then ask for the spelling of 1e6. I would do:

if isinstance(number, float):
     if number % 1 == 0:
         number = int(number)
     else:
         raise ValueError("number must be an integer")

elif not isinstance(number, int):
     raise ValueError("number must be a number")


Note that your function would happily accept things like 1j, "abc", None...
For Guido's beard, don't roll your own log function! use int(math.log10)