fluttersmith/py-snippetsGitHub ↗

Convert an integer to Roman numerals

Convert 1 to 3999 into Roman numerals with a greedy table, rejecting values outside that range.

Numbers#conversionO(1) time✓ 3 examples pass on 3.11–3.14

Chapter numbers, clock faces, movie sequels.

to_roman_numeral.py✓ ruff · mypy --strict
NUMERALS = [
    (1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),
    (100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
    (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I"),
]  # fmt: skip


def to_roman_numeral(number: int) -> str:
    """Convert an integer from 1 to 3999 to a Roman numeral."""
    if not 1 <= number <= 3999:
        raise ValueError(f"Roman numerals cover 1 to 3999, got {number}")
    parts = []
    for value, numeral in NUMERALS:
        count, number = divmod(number, value)
        parts.append(numeral * count)
    return "".join(parts)
  1. how many times this numeral fits, and what's left over
examples✓ 3 doctests · 3.11–3.14
passed on Python 3.11 to 3.14: >>> to_roman_numeral(11)'XI'passed on Python 3.11 to 3.14: >>> to_roman_numeral(1998)'MCMXCVIII'passed on Python 3.11 to 3.14: >>> to_roman_numeral(0)Traceback (most recent call last):...ValueError: Roman numerals cover 1 to 3999, got 0
Edit the function or the examples and run the doctests here, on Python 3.14. Python loads only when you click (about 7 MB, cached after the first time).

How it works

  • The table lists every value with its numeral, largest first, including the subtractive pairs like 900 = CM.
  • divmod says how many times each value fits and what’s left over.
  • Standard Roman numerals have no zero and stop at 3999, so anything else raises.

Complexity: O(1) time: at most 13 steps

Importfrom pysnippets.numbers.to_roman_numeral import to_roman_numeralNo dependencies: copy to_roman_numeral.py into your project, or clone the repo.

Adapted from to-roman-numeral in 30-seconds-of-python by the 30 seconds of code contributors, CC BY 4.0. Changed: Validates the range; builds the result with join; typed. Suggest an edit.