Library tutorials & articles

Number Systems

Roman Numerals

    And finally, we come to Roman Numerals.  They are roughly equivelent to something that we would write as "100 + 50 + 10 + 1".  In other words, they are baseless.

'The following are  the 14 different valid letter combinations_
'for Roman Numerals
'and what they equal.  It is improper (for whatever reason) to_
'say "IM = 999",
'you must say "CMXCIX".
Public Enum RomanNumerals As Integer
N = 0
I = 1
IV = 4
V = 5
IX = 9
X = 10
XL = 40
L = 50
XC = 90
C = 100
CD = 400
D = 500
CM = 900
M = 1000
End Enum

Public Function FromRomanNumeral(ByVal Number As String) As Long
Number = Number.ToUpper

Dim chrRomans() As Char = Number.ToCharArray
Dim intRomanParse(Number.Length) As Integer
Dim lRoman As Long
Dim i As Integer
Dim iCounter As Int16
         
'Basically, steps through the Roman Numeral string looking for
'valuesin the RomanNumerals Enum
For i = Number.Length - 1 To 0 Step -1
Select Case chrRomans(i).ToString
Case "N" '0      
intRomanParse(i) = RomanNumerals.N

Case "I" '1
intRomanParse(i) = RomanNumerals.I

Case "V" '5
If i > 0 AndAlso chrRomans(i - 1).ToString = "I" Then
intRomanParse(i) = RomanNumerals.IV    
'Used if an extended digit is present since they use_
'2 letters     
i -= 1
Else  
intRomanParse(i) = RomanNumerals.V
End If

Case "X" '10
If i > 0 AndAlso chrRomans(i - 1).ToString = "I" Then
intRomanParse(i) = RomanNumerals.IX   
'Used if an extended digit is present since they use_
'2 letters        
i -= 1
Else
intRomanParse(i) = RomanNumerals.X  
End If

Case "L" '50
If i > 0 AndAlso chrRomans(i - 1).ToString = "X" Then
intRomanParse(i) = RomanNumerals.XL   
'Used if an extended digit is present since they use_
'2 letters
i -= 1
Else
intRomanParse(i) = RomanNumerals.L
End If

Case "C" '100      
If i > 0 AndAlso chrRomans(i - 1).ToString = "X" Then
intRomanParse(i) = RomanNumerals.XC     
'Used if an extended digit is present since they use_
'2 letters 
i -= 1    
Else   
intRomanParse(i) = RomanNumerals.C
End If

Case "D" '500                     
If i > 0 AndAlso chrRomans(i - 1).ToString = "C" Then
intRomanParse(i) = RomanNumerals.CD         
'Used if an extended digit is present since they use_
'2 letters           
i -= 1      
Else
intRomanParse(i) = RomanNumerals.D
End If

Case "M" '1000     
If i > 0 AndAlso chrRomans(i - 1).ToString = "C" Then
intRomanParse(i) = RomanNumerals.CM
'Used if an extended digit is present since they use_
'2 letters 
i -= 1 
Else
intRomanParse(i) = RomanNumerals.M
End If

Case Else 'Not a digit    
Throw New Exception(Number & " is not a valid Roman_
Numeral because it contains the symbol:_
" & chrRomans(i).ToString & ".")
End Select

Next

For i = 0 To Number.Length
lRoman += intRomanParse(i)
Next

Return lRoman
End Function
Public Function ToRomanNumeral(ByVal Number As Long) As String
Dim strRoman As String
Dim intM, intCM, intD, intCD, intC, intXC, intL, intXL, intX,_
intIX, intV, intIV, intI As Short
Dim i As Short

If Number = 0 Then Return "N"

'How many integral times does "M" go into the number?
intM = Number \ RomanNumerals.M
Number -= intM * RomanNumerals.M

'How many integral times does "CM" go into the number?
intCM = Number \ RomanNumerals.CM
Number -= intCM * RomanNumerals.CM

'How many integral times does "D" go into the number?
intD = Number \ RomanNumerals.D
Number -= intD * RomanNumerals.D

'How many integral times does "CD" go into the number?
intCD = Number \ RomanNumerals.CD
Number -= intCD * RomanNumerals.CD

'How many integral times does "C" go into the number?
intC = Number \ RomanNumerals.C
Number -= intC * RomanNumerals.C

'How many integral times does "XC" go into the number?
intXC = Number \ RomanNumerals.XC
Number -= intXC * RomanNumerals.XC

'How many integral times does "L" go into the number?
intL = Number \ RomanNumerals.L
Number -= intL * RomanNumerals.L

'How many integral times does "XL" go into the number?
intXL = Number \ RomanNumerals.XL
Number -= intXL * RomanNumerals.XL

'How many integral times does "X" go into the number?
intX = Number \ RomanNumerals.X
Number -= intX * RomanNumerals.X

'How many integral times does "IX" go into the number?
intIX = Number \ RomanNumerals.IX
Number -= intIX * RomanNumerals.IX

'How many integral times does "V" go into the number?
intV = Number \ RomanNumerals.V
Number -= intV * RomanNumerals.V

'How many integral times does "IV" go into the number?
intIV = Number \ RomanNumerals.IV
Number -= intIV * RomanNumerals.IV

'How many integral times does "I" go into the number?
intI = Number \ RomanNumerals.I
Number -= intI * RomanNumerals.I


'Puts together all the pieces
For i = 1 To intM
strRoman &= "M"
Next

For i = 1 To intCM
strRoman &= "CM"
Next

For i = 1 To intD
strRoman &= "D"
Next

For i = 1 To intCD
strRoman &= "CD"
Next

For i = 1 To intC
strRoman &= "C"
Next

For i = 1 To intXC
strRoman &= "XC"
Next

For i = 1 To intL
strRoman &= "L"
Next

For i = 1 To intXL
strRoman &= "XL"
Next

For i = 1 To intX
strRoman &= "X"
Next

For i = 1 To intIX
strRoman &= "IX"
Next

For i = 1 To intV
strRoman &= "V"
Next

For i = 1 To intIV
strRoman &= "IV"
Next

For i = 1 To intI
strRoman &= "I"
Next

Return strRoman
End Function

'This function will take an incorrectly formated Roman Numeral and_
'return it in it's corrected form ("CMXCIXVIII" becomes "MVII")
Public Function CorrectRomanNumeral(ByVal Number As String) As String
Dim lRoman As Long = FromRomanNumeral(Number)
Return ToRomanNumeral(lRoman)
End Function
AddThis

Comments

  1. 11 Sep 2007 at 04:29

    I have just returned from an extended absence from this forum so I realize this is rather late.

    It looks good, the only thing I can spot is the line where you catch the Exception.  Not finding an extended numeral is expected (and common at that), so an Exception shouldn't be used to control the flow of code.

    Although I can't think of an easy way around it since they removed Enum.TryParse (2.0 and later I think).







  2. 24 May 2007 at 21:10

    Thanks for the Roman Numeral example! I made some equivalent functions in C# using Enum's static methods. This results in more concise versions of the two primary methods described in the article. They have been named a little bit differently to suit my tastes, but are the same methods in terms of the algorithm used. This code also uses the exact same RomanNumerals enum (which I haven't included in this snippet). Feel free to suggest further improvements, or to translate this back to VB.NET:

          
            private int ParseRomanNumeral(string numeral)
            {
                numeral = numeral.ToUpper();

                char[] characters = numeral.ToCharArray();
                int[] parseBuf = new int[numeral.Length];
                int result = 0;
                bool bSkip = false;
                for (int i = numeral.Length - 1; i >= 0; i--)
                {
                    if (bSkip)
                    {
                        bSkip = false; // Skip this digit because it is part of a 2-digit numeral
                        continue;
                    }
                    parseBuf[i] = (int)Enum.Parse(typeof(RomanNumerals), characters[i].ToString());
                    if (parseBuf[i] % 5 == 0 && i > 0)
                    {
                        string extended = characters[i-1].ToString() + characters[i].ToString();
                        try
                        {
                            parseBuf[i] = (int)Enum.Parse(typeof(RomanNumerals), extended);
                            bSkip = true;
                        }
                        catch(ArgumentException)
                        {
                            // Ignore this exception. It just means we didn't find an extended numeral
                        }
                    }
                }

                for (int i = 0; i < numeral.Length; i++)
                {
                    result += parseBuf[i];
                }
                return result;
            }

            private string ConvertToRomanNumeral(int num)
            {
                string sResult = "";

                sResult = Enum.GetName(typeof(RomanNumerals), num);
                if (sResult == null)
                {
                    string[] names = Enum.GetNames(typeof(RomanNumerals));
                    int[] vals = (int[])Enum.GetValues(typeof(RomanNumerals));
                    for (int i = names.Length - 1; i > 0; i--) // Don't process the 0th element, since it is 0 and results in bad math
                    {
                        int temp = num / vals[i];
                        num -= temp * vals[i];
                        for (int j = 0; j < temp; j++)
                        {
                            sResult += names[i];
                        }
                    }
                }

                return sResult;
            }





























































Leave a comment

Sign in or Join us (it's free).

Related discussion