[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky]
[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Convert between Arabic and Roman numerals in Python

[Values converted between Roman and Arabic numerals in Python]

For a nice discussion of Roman numerals, see Roman Numerals, How they work. That page explains the basic rules and more advanced rules such as using parentheses or an overline to multiply values by 1,000. It includes a lot of interesting historical notes that leave you feeling that the Romans basically made up and broke the rules freely as they went along. The story about Sulpicius Galba's inheritance is particularly interesting.

Another good page is Roman Numerals. It has some explanation and background, plus some notes on pronunciation.

This example includes methods that convert between Arabic and Roman numerals. It follows most of the rules you probably know from elementary school. The only unusual issue is that it uses parentheses to multiply by 1,000. In the value (IV)CCCXXI, for example, the parentheses mean the IV part should be multiplied by 1,000 giving 4,000, so the total value is 4321.

Roman to Arabic

The program uses the following code to convert a string containing Roman numerals into an integer.

# Convert Roman numerals to an integer. def roman_to_arabic(roman): # Initialize the letter map. char_values = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000, } if len(roman) == 0: return 0 roman = roman.upper() # See if the number begins with (. if roman[0] == '(': # Find the closing parenthesis. pos = roman.rfind(')') # Get the value inside the parentheses. part1 = roman[1:pos] part2 = roman[pos + 1:] return 1000 * roman_to_arabic(part1) + roman_to_arabic(part2) # The number doesn't begin with (. # Convert the letters' values. total = 0 last_value = 0 for i in range(len(roman) - 1, -1, -1): new_value = char_values[roman[i]] # See if we should add or subtract. if new_value < last_value: total -= new_value else: total += new_value last_value = new_value # Return the result. return total

When the roman_to_arabic method starts, it initializes the char_values dictionary to store the values of the basic Roman numerals: I = 1, V = 5, etc.

Next, if the input string begins with an open parenthesis, the method finds the corresponding closing parenthesis and separates the string into the piece inside the parentheses and the piece that comes after the closing parenthesis. For example, it breaks the input string (IV)CCCXXI into the pieces IV and CCCXXI. The method then calls itself recursively to evaluate the two pieces, multiples the first by 1,000, and adds them together.

If the input doesn't begin with an open parenthesis, the code loops through the letters from right-to-left. For each letter, it uses the char_values dictionary to get the letter's value. If that value is smaller than the value of the previous letter, it subtracts the value from the total. For example, if the input contains IV, it subtracts the I.

If the letter's value is not smaller than the previous value, it adds the value. For example, if the input contains VI, it adds the V.

When it has finished reading all of the letters, the method returns the result.

Arabic to Roman

The program uses the following code to convert an integer into Roman numerals.

# Convert Roman numerals to an integer. def arabic_to_roman(arabic): # Map digits to letters. thou_letters = [ '', 'M', 'MM', 'MMM' ] hund_letters = [ '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM' ] tens_letters = [ '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC' ] ones_letters = [ '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' ] # See if it's >= 4000. if arabic >= 4000: # Use parentheses. thou = arabic // 1000 arabic %= 1000; return '(' + arabic_to_roman(thou) + ')' + arabic_to_roman(arabic) # Otherwise process the letters. result = '' # Pull out thousands. num = arabic // 1000 result += thou_letters[num] arabic %= 1000 # Handle hundreds. num = arabic // 100 result += hund_letters[num] arabic %= 100 # Handle tens. num = arabic // 10 result += tens_letters[num] arabic %= 10 # Handle ones. result += ones_letters[arabic] return result

The string arrays thou_letters, hund_letters, and so on hold the Roman numeral representations for various Arabic digits. For example, hund_letters[3] contains the Roman numeral representation for one to nine hundred. Notice that the 0 entry in all of the arrays is an empty string because the Romans had no notation for 0.

The arabic_to_roman method first checks whether the input value is greater than or equal to 4,000, in which case the method will use parentheses to multiply a value by 1,000. In that case, the code separates the value into two pieces: The original value divided by 1,000 and the remainder. For example, if the input is 54,321, the code separates it into the pieces 54 and 321. The method calls itself recursively to convert the pieces into Roman numerals, adds parentheses around the first piece's representation, and returns the combined results. In this example, it would return (54)321 with the two pieces converted into Roman numerals, or (LIV)CCCXXI.

If the input value is less than 4,000, the method pulls out the value's thousands. It calculates the number of thousands in the value, uses that number as an index into the thou_letters array, and adds the corresponding array value to the result string.

The method repeats the same steps for the value's hundreds, tens, and ones digits. When it finishes, it simply returns the result.

Conclusion

The example program uses the following code to test the roman_to_arabic and arabic_to_roman functions.

for arabic in range(1, 20): roman = arabic_to_roman(arabic) print(f'{arabic} -> {roman} -> {roman_to_arabic(roman)}') values = [2025, 1234, 4321, 1234567890, 9876543210] for arabic in values: roman = arabic_to_roman(arabic) print(f'{arabic} -> {roman} -> {roman_to_arabic(roman)}')

The code first loops through the values 1 to 19. It prints each value, its Roman numeral equivalent, and then the Roman numerals converted back into Arabic numerals.

The program then repeats those steps for some selected numbers.

The functions work well for the most obvious cases and they handle many odd cases. For example, if you enter the Roman numerals XIIV, the program produces the Arabic value 13 and then converts that into the Roman numerals XIII. I think a typical Roman would understand that XIIV means 13.

There are some really weird inputs that the program doesn't handle. For example, the roman_to_arabic function understands properly nested parentheses but not unnested parentheses. For instance, it can't understand (M)(V) even though a person could probably figure out that this means 1,005,000. You can fix the function to handle non-nested parentheses if you like.

Download the example to experiment with it and to see additional details.

© 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.