4
\$\begingroup\$

After implementing most of the suggestions from the first question, This is the code I got:

from math import log

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

# Powers of a thousand (US system of naming)
suffixes = ("zero", "thousand", "million", "billion", "trillion",
            "quadrillion", "quintillion", "sextillion", "septillion",
            "octillion", "nonillion", "decillion", "undecillion",
            "duodecillion", "tredecillion", "quattuordecillion",
            "quinquadecillion", "sedecillion", "septendecillion",
            "octodecillion", "novendecillion", "vigintillion",
            "unvigintillion", "duovigintillion", "tresvigintillion",
            "quattuorvigintillion", "quinquavigintillion", "sesvigintillion",
            "septemvigintillion", "octovigintillion", "novemvigintillion",
            "trigintillion", "untrigintillion", "duotrigintillion",
            "trestrigintilion", "quattuortrigintillion",
            "quinquatrigintillion", "sestrigintillion", "septentrigintillion",
            "octotrigintillion", "noventrigintillion", "quadragintillion")

def spell_out(number):
    """Returns a string representation of the number, in the US system"""
    try:
        number = int(number)
    except OverflowError:
        # This will be triggered with large float values such as 1e584
        return "infinity"

    if number < 0:
        return "negative " + spell_out(-1 * number)

    if number < 20:
        return zero_to_nineteen[number]

    if number < 100:
        tens_digit, ones_digit = divmod(number, 10)
        return _createNumber(tens[tens_digit], ones_digit, "-")

    if number < 1000:
        hundreds_digit, rest = divmod(number, 100)
        return _createNumber(spell_out(hundreds_digit) + " hundred", rest)

    suffix_index = int(log(number, 1000))

    if suffix_index < len(suffixes):
        suffix_value = 1000 ** suffix_index
        prefix, rest = divmod(number, suffix_value)
        prefix = spell_out(prefix) + " " + suffixes[suffix_index]
        return _createNumber(prefix, rest, ", ")

    return "infinity"

def _createNumber(prefix, suffix, seperator=" "):
    # Returns a number with given prefix and suffix (if needed)
    if not isinstance(prefix, str):
        prefix = spell_out(prefix)
    return prefix if suffix == 0 else prefix + seperator + spell_out(suffix)

I'd like to hear any comments about readability/performance/pythonicity and anything else you can think about that could use improving.

\$\endgroup\$
  • \$\begingroup\$ One tiny nitpick: in the definition of _createNumber, the variable seperator should be spelt separator. \$\endgroup\$ – alexwlchan Aug 14 '14 at 13:54
  • \$\begingroup\$ Your comment this will be triggered with large float values such as 1e584 isn't exactly correct, as you're actually using int instead of float. \$\endgroup\$ – James Mertz Aug 14 '14 at 22:57
  • \$\begingroup\$ 1e584 is a float. int(1e584) will raise a OverflowError. Therefore, I am correct in saying that the error will be triggered with large float arguments. \$\endgroup\$ – mleyfman Aug 14 '14 at 23:18
2
\$\begingroup\$

math.log works in floating point and thus has limited precision. On my system at least, your code goes to infinite recursion from spell_out(10**15-1) for example. To avoid this, I would break the number into groups using divmod in a loop.

\$\endgroup\$
  • \$\begingroup\$ Annoyingly enough, I did this for the first attempt, but then received a comment that I shouldn't hand-roll my own log function :| \$\endgroup\$ – mleyfman Aug 14 '14 at 15:00
2
\$\begingroup\$

It looks pretty nice! I can only nitpick.

Instead of:

return "negative " + spell_out(-1 * number)

You could simply:

return "negative " + spell_out(-number)

Some PEP8 recommendations:

  • Use 2 blank lines before each function definition
  • Use snake case in function names, _create_number instead of _createNumber
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.