This week while I was solving a Project Euler problem, I came up with a simple roman to decimals converter. I will show its implementation in Python in this post.
Roman numerals
Romans represented numbers using numerals as follows:
I - 1 V - 5 X - 10 L - 50 C - 100 D - 500 M - 1000
We will consider only three rules to convert them to decimal numbers.
- Numerals must be written in decreasing order.
For example, MD is a valid representation of 1500 while DM isn’t. - The total value is obtained by adding each successive decreasing numeral to the right.
For example, VIII represents 8 (5 + 1 + 1 + 1). See the numerals value decrease from right to left. - As an exception to rule 1., it’s possible to decrease I, X and C from the two next numerals with greater values. To do it, we write the lower numeral left of the greater one.
For example, we can decrease X from both L and C:
XL = 40 (50 – 10) and XC = 90 (100 – 10)
However, we can’t subtract X from other greater numerals:
XM = 990 (1000 – 10), isn’t a valid roman numeral.
A roman to decimal converter
For our converter, we will take advantage that roman numerals are written in decreasing order by analyzing it starting from the end. Then we will add the corresponding decimal value of each numeral using a Python dictionary.
We will consider the subtraction of numerals, such as I in IV, by the fact that the left hand side numeral is lower than the right hand side one (I < V). In this case, I in IV will be parsed as -1 instead of +1.
The dictionary used to convert roman numerals do decimals:
roman_dict ={'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
See that roman_dict['D']
will return 500
and so on.
The parsing will be implemented using function decimal
:
def decimal(roman): """ roman: a string or roman numerals. Returns the equivalent in decimal numbers (int). """ global roman_dict # Analyze string backwards roman_back = list(reversed(list(roman))) value = 0 # To keep track of order right_val = roman_dict[roman_back[0]] for numeral in roman_back: left_val = roman_dict[numeral] # Check for subtraction if left_val < right_val: value -= left_val else: value += left_val right_val = left_val return value
As it was stated before, all we are doing is getting the decimal value of each numeral:
left_val = roman_dict[numeral]
And then verifying if we have to add or subtract the value of that numeral by checking if the left value is lower than the right one:
if left_val < right_val: ...
Let’s check it works with a rather complicated roman numeral:
>>> decimal('MCDXLIX') 1449
It works!
Finally, we have to remark that we’re assuming that the roman numerals are written correctly. This converter will retun a value for any possible combination of roman numerals, even invalid ones such as IXM.