Computer Science 101

with:  Erik Oosterwal

Search for specific programs or algorithms:
Custom Search





Decimal Value to Roman Numeral Conversion
and
Roman Numeral to Decimal Value Conversion


Roman numerals follow a fairly strict rule in sequence in order to reduce ambiguity in numbers.  To create Roman numerals we must first define which letters represent which values, and what exceptions there are to the basic rule.

First, the following letters represent the associated values:


I =

1

V =

5

X =

10

L =

50

C =

100

D =

500

M =

1000


The basic rule states that numerals are read from left to right, larger valued letters precede smaller valued letters, and multiple letters in a row represent multiples of that value.  For instance:


I = 1
II = 2
III = 3
VIII = 8
CVIII = 108
CCVIII = 208
DCCVIII = 708
MDCCVIII = 1708


The single exception to the basic rule is that if a larger value letter is preceded by a single smaller value letter, then the combination represents the value of the larger value letter minus the value of the smaller value letter.  This exception only holds true for the values 4, 9, 40, 90, 400, and 900, which are written as IV, IX, XL, XC, CD, and CM respectively.

A seldom used convention of Roman numerals is that you can create values larger values by overscoring letters to represent the letter's normal value multiplied by 1000:


_
V =


5000

_
X =


10,000

_
L =


50,000

_
C =


100,000

_
D =


500,000

_
M =


1,000,000


We won't bother with the larger values in this routine and limit our numbers to values between 1 and 3999.



DECIMAL to ROMAN Algorithm 1

The following pseudo code should provide enough information to implement the algorithm in your language of choice:



function roman(InputValue) as string

dim InputValue as integer		' Declare Variables
dim RomanValue as string

if (InputValue > 3999) OR (InputValue < 1)
	roman = "N/A"
	return
endif

RomanValue = ""

While InputValue > 999 (
	RomanValue = RomanValue + "M"	' Concatenate the letters to the right side
	InputValue = InputValue - 1000	' Reduce the amount left in InputValue
	)

If InputValue > 899 (
	RomanValue = RomanValue + "CM"	' Concatenate letters to the right side
	InputValue = InputValue - 900
	)

If InputValue > 499 (
	RomanValue = RomanValue + "D"
	InputValue = InputValue - 500
	)

If InputValue > 399 (
	RomanValue = RomanValue + "CD"
	InputValue = InputValue - 400
	)

While InputValue > 99 (
	RomanValue = RomanValue + "C"
	InputValue = InputValue - 100
	)

If InputValue > 89 (
	RomanValue = RomanValue + "XC"
	InputValue = InputValue - 90
	)

If InputValue > 49 (
	RomanValue = RomanValue + "L"
	InputValue = InputValue - 50
	)

If InputValue > 39 (
	RomanValue = RomanValue + "XL"
	InputValue = InputValue - 40
	)

While InputValue > 9 (
	RomanValue = RomanValue + "X"
	InputValue = InputValue - 10
	)

If InputValue > 8 (
	RomanValue = RomanValue + "IX"
	InputValue = InputValue - 9
	)

If InputValue > 4 (
	RomanValue = RomanValue + "V"
	InputValue = InputValue - 5
	)

If InputValue > 3 (
	RomanValue = RomanValue + "IV"
	InputValue = InputValue - 4
	)

While InputValue > 0 (
	RomanValue = RomanValue + "I"
	InputValue = InputValue - 1
	)

roman = RomanValue

return
	
Discuss this algorithm at the Computer Algorithms blog page.




DECIMAL to ROMAN Algorithm 2

I've come up with a shorter algorithm (1-18-2008) that should be easy enough to understand. This new algorithm does away with all the cumbersome IF statements from the previous algorithm and wraps it all up into one WHILE loop. It's still the same principle as that used in the previous algorithm, but instead of hard coding all the values of the Roman Numeral characters and pairs into separate IF and WHILE clauses, the values and characters are stored in a couple of arrays and referenced in a single WHILE loop. I don't know if this new code is faster than the previous algorithm; I'll have to code them both and do some tests--that can wait for another day. For now, here's the updated code written in VB .NET:



    Private Sub cmdConvert_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdConvert.Click

        ' Declare and initialize all variables.  Roman Numerals and their standard combinations
        '   are stored in strRoman.  The corresponding values are stored in intDecimal.

        Dim strRoman() As String = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}
        Dim intDecimal() As Integer = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
        Dim intInput As Integer = Nothing           ' Numeric representation of the input field.
        Dim intTemp As Integer = Nothing            ' Used to store the magnitude of each decimal digit.
        Dim intPointer As Short = 0                 ' Pointer to the Roman and Decimal arrays, starts at the first cell.
        Dim intCountI As Short = Nothing            ' Temporary variable used for loops.

        intInput = CInt(txtInput.Text)              ' Convert the string in the input field to an integer.
        txtOutput.Clear()                           ' Clear the contents of the output field.

        While intInput > 0                                  ' Check to see if intInput still has any value left in it.
            intTemp = intInput \ intDecimal(intPointer)     ' See how many of the currently selected value can fit in the remaining input.

            For intCountI = 1 To intTemp
                txtOutput.Text += strRoman(intPointer)      ' Append a number of Roman Characters depending on the value of intTemp.
            Next

            intInput -= intTemp * intDecimal(intPointer)    ' Subtract the value of the characters that were appended to output from the input.
            intPointer += 1                                 ' Move the pointer to the next cell of the arrays.
        End While

    End Sub
Discuss this algorithm at the Computer Algorithms blog page.


You can download a ZIP file with all the VB .NET project files and an executable here.




DECIMAL to ROMAN Algorithm 3 - MCXI

It ocurred to me, after designing and implementing the previous algorithm, that if you're willing to sacrifice a bit more memory space for a table, then there's a faster way to convert from Decimal to Roman Numeral notation. The following code shows an implementation of the algorithm in VB .NET. I call it the MCXI algorithm due to the values of the four rows in the table. There are some funny things going on between the 0 based indexing used in accessing array data and 1 based indexing used in the MID function used for parsing the input string. You may need to address this if you use a different programming language.



    Private Sub btnConvert_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConvert.Click

        ' Algorithm MCXI
        ' Written by: Erik Oosterwal
        ' Written on: 1-21-2008
        '
        ' Routine to convert Decimal values to Roman Numeral notation.  This algorithm uses a table to store
        '   all the standard Roman Numeral notation for each digit in each Base-10 digit position (ones, tens,
        '   hundreds, thousands).  Each Decimal character is then evaluated to find its magnitude and extract
        '   the corresponding Roman Numeral notation for that value and prepend it to the output string.
        '
        ' This routine does not check for proper usage - the input is not checked for valid numeral characters
        '   and it does not check to verify that the values are positive values between 1 and 4000.  To make
        '   function robust, you check put checks in place for these conditions.

        'Store the Roman codes for each Decimal digit.
        Dim strRoman(,) As String = {{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}, _
                                     {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}, _
                                     {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}, _
                                     {"", "M", "MM", "MMM", "MMMM", "", "", "", "", ""}}

        Dim strOutput As String = Nothing               ' Initialize the output string to blank.
        Dim intLoopI As Integer = Nothing               ' Counter used for the Loop
        Dim intStrLength As Integer = Nothing           ' The length of the input string.

        intStrLength = Len(Me.txtInput.Text)            ' Find out how many digits are in the input number (magnitude).

        For intLoopI = intStrLength To 1 Step -1        ' MID function uses 1 based indexing for the string pointer.

            strOutput = strRoman(intStrLength - intLoopI, Val(Mid$(Me.txtInput.Text, intLoopI, 1))) & strOutput

        Next

        Me.txtOutput.Text = strOutput

    End Sub


As you can see, algorithm number three (MCXI Algorithm) executes the loop a maximum of 4 times and uses no division or multiplication. This should speed up the run time a great deal over the previous two listings.

You can download an executable along with all the VB .NET project files for this algorithm here.





ROMAN NUMERAL to DECIMAL Algorithm 1

Ok, I've promised a way to convert Roman Numeral back to Decimals for a very long time, and I'm finally getting around to showing one method of doing that.

Start at the left-most character in the Roman Numeral string and work your way to the last character one character at a time. Add the value of the current character to the total. If the current character has a larger value than the previous character, then subtract 2x the value of the previous character from the total.

For instance, XIX is equal to 19. Start at the first character and add 10 to the total; the total is now 10. Go to the second character and add 1 to the total; the total is now 11. Go to the third character and add 10 to the total; the total is now 21, but... Because the current character has a larger value than the previous character we subtract 2x the value of the previous character, 1, from the total; the total is now 19. There are no more characters so we stop.



Function Roman2Decimal(strInput as string) as integer

   Dim intValues() as integer                ' Reserve space for an array.
   Dim intLoopI as integer = nothing         ' Initialize the loop counter to 0.
   Dim intTotal as integer = nothing         ' Clear the total.
   Dim intPreviousPointer as integer = 99999 ' Set the value of the previous pointer to some MAX value.
   Dim intCurrentPointer as integer = nothing  ' This will hold the value of the current Roman Numeral character.

   intValues(M) = 1000             ' This array addressing method
   intValues(D) = 500              '  assumes that the language you
   intValues(C) = 100              '  are using allows you to use
   intValues(L) = 50               '  values other than integer values
   intValues(X) = 10               '  to access data in an array.
   intValues(V) = 5
   intValues(I) = 1

   For intLoopI = 0 to (len(strInput)-1)   ' Assuming 0 indexing...

      intCurrentPointer = intValues(mid$(strInput,intLoopI,1)) ' Get the value of the current Roman Numeral character
      intTotal = intTotal + intCurrentPointer                  ' Add the value to the total.

      If intCurrentPointer > intPreviousPointer then       ' If the current value is larger than the previous, then
         intTotal = intTotal - (2 * intPreviousPointer)    '  subtract twice the value of the previous character from the total.
      EndIf

      intPreviousPointer = intCurrentPointer      ' Move the value of the current character to be the
                                                  '  value of the previous character and get then next one.

   Next

   Roman2Decimal = intTotal

End Function


There is one small glitch with this algorithm--the rules for writing Roman Numerals does not allow for three characters in a row of increasing value, like IXC. This number is a bit ambiguous... ...Does it represent the value 99 (IX=9 + XC=90 = 99), or 91 (IX=9, and 9 less than C=100 is 91), or 89, as this algorothm produces (add 1 for I (1), then add 10 for X (11), but subtract 2 because it's a larger character (9), then add 100 for C (109), but subtract 20 because it's a larger character (89)). You can also look at it this way... whenever you have a larger value following a smaller value, then it's like starting with the right most of the characters and subtracting the value of the character to its immediate left. IX = 10-1 = 9, XC = 100-10 = 90, or a bit unconventionally, IC = 100-1 = 99, or even IM = 1000-1 = 999. If you follow this rule, then IXC would be 100-10-1 or 89, which is just what this algorithm provides. Incidentally, this algorithm can also handle the other cases, IC, IM, etc.

I'll get this algorithm coded and verify everything's kosher and provide some VB or VB. NET project files for your use as soon as possible.




ROMAN NUMERAL to DECIMAL Algorithm 2

Here's a second algorithm for converting Roman Numeral notation to a Decimal value:

Start at the right-most character. As long as the value of current character is the same size or larger than then previous character, then add that value to the total. If the value of the current character is smaller than the previous character then subtract that value from the total. This algorithm produces the same output as the first.

Here is a VB .NET implementation of the algorithm:



Private Sub btnConvert_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConvert.Click

        ' Roman2Decimal - 2
        '
        ' Written by:  Erik Oosterwal
        ' Written on:  1 - 21 - 2008
        '
        ' Working your way from the least significant Roman Numeral (right side of the string)
        '   to the most significant, when you encounter a value higher than the previous
        '   value, add that value to the total.  when you encounter a smaller value, subtract
        '   that value from the total.
        '
        ' Some programming languages, such as PHP, allow you to use strings as keys to an array.
        '   Using a language like that will let you remove all the ASC() references, and also
        '   reduce the size of the intValues array to 7 cells.

        Dim intValues(100) As Integer               ' Reserve space for an array.
        Dim intLoopI As Integer = Nothing           ' Initialize the loop counter to 0.
        Dim intTotal As Integer = Nothing           ' Clear the total.
        Dim intPreviousPointer As Integer = 0       ' Set the value of the previous pointer to some MIN value.
        Dim intCurrentPointer As Integer = Nothing  ' This will hold the value of the current Roman Numeral character.

        intValues(Asc("M")) = 1000
        intValues(Asc("D")) = 500
        intValues(Asc("C")) = 100
        intValues(Asc("L")) = 50
        intValues(Asc("X")) = 10
        intValues(Asc("V")) = 5
        intValues(Asc("I")) = 1

        For intLoopI = Len(Me.txtInput.Text) To 1 Step -1    ' Assume 1 indexing

            intCurrentPointer = intValues(Asc(Mid$(Me.txtInput.Text, intLoopI, 1).ToUpper))

            If intCurrentPointer < intPreviousPointer Then
                intTotal = intTotal - intCurrentPointer
            Else
                intTotal = intTotal + intCurrentPointer
            End If

            intPreviousPointer = intCurrentPointer

        Next

        Me.txtOutput.Text = Str(intTotal)

    End Sub


There's a little less math involved in the second algorithm, which means that it should execute a bit faster. In the first Roman 2 Decimal algorithm, every instance of the right-character being larger than the left-character (IV, IX, XC, etc) resulted in the total being acted upon twice. With this latter algorithm, the total is acted upon only once for each character in the input string no matter if it's larger or smaller than the previous one.

You can download an executable along with the VB .NET project in a zip file here.


I'll get all the algorithms described on this page coded as soon as time permits and provide a comparison of the relative speed of each.






Discuss computer algorithms and other computer science topics at the Computer Algorithms blog page.

All code and original algorithms are © Erik Oosterwal - 1987-2008
Computer Science 101

Dressing for Success