Building a Visual Basic Roman Numeral Converter

Introduction

A popular question on developer forums is how to create a Roman Numeral converter. It may seem to be a relatively easy task, but looks are deceiving, indeed. Today, I will show you a few different ways to create a decent Roman Numeral converter with Visual Basic.

First, Some History

A Roman Numeral is a number that is written in the way that the ancient Romans used to write numbers. This numeric system stayed the way of writing numbers in Europe until the Middle Ages. The Roman numerical system is composed of seven Latin letters which include: I (unus): one; V (quinque): five; X (decem): ten; L (quinquaginta): fifty; C (centum): one hundred; D (quingenti): five hundred; and M (mille): one thousand. By combining symbols, a Roman number is formed. There is no symbol for a zero, so there is also no need for zeros to keep place for other numbers. For example: A number such as 203 is written as CCIII.

If two letters are next to each other, each letter is worth powers of ten and the second letter is worth ten times the first. The ultimate value of this letter-group, or number, equals the second letter's value subtracted by the first letter. This means then that IX equals nine, XC is ninety, and CM becomes nine hundred.

The Roman Numeral system is additive; this means that the numbers are simply added. For example, the number 'IV' means four. Talking about the Roman number four, this number is written like that for the simple reason that four or more symbols that look the same aren't allowed to be written out. This means that you cannot have a number written as IIII—which ordinarily would mean four—but written as IV instead.

Enough history? Okay, let me move on. Let's create a project that can convert Roman Numerals to ordinary numbers.

Create a new Visual Basic Windows Forms project and add one button to the form. Add a separate class. Feel free to use your own names, but remember that my names might be different than yours.

Let me pause here and explain the methodology first.

Man, I love patterns! If you look closely, you will see the same pattern being repeated depending on what the value of the number is. Before I get too far ahead of myself, look at the following:

1 I

2 II

3 III

4 IV

5 V

6 VI

7 VII

8 VIII

9 IX

10 X

10 X

20 XX

30 XXX

40 XL

50 L

60 LX

70 LXX

80 LXXX

90 XC

100 C

100 C

200 CC

300 CCC

400 CD

500 D

600 DC

700 DCC

800 DCCC

900 CM

1000 M

Every sequence starts with a unique symbol as the first in the sequence, so that means 1 = I, 10 = X, 100 = C. The second in the sequence has two of the same symbol, and the third in the numeric sequence has three. Then, with the fourth item, it shows the first number in the current sequence, plus the next unique symbol. The fifth shows the unique symbol for 5, 50, 500, and so on. Six through eight follow the same sequence as one to three. At nine, there is again a change as well as 10.

Seeing this pattern allows you to better think of a solution. Always look long and hard at a situation; you will find answers…

Now, to put this newfound knowledge of this pattern as well as understanding that we only have seven unique symbols to work with, is quite easy, but it is a lot of work.

Let's Add the First Class

This class is ultimately responsible for the input of ordinary numbers and the output of Roman numbers.

Public Class InputOutput

   Private intIn As Integer
   Private strOut As String

   Public Sub New(intInput As Integer)

      Me.intIn = intInput

   End Sub

   Public Property Input() As Integer

      Get

         Return intIn

      End Get

      Set(value As Integer)

         intIn = value

      End Set

   End Property

   Public Property Output() As String

      Get

         Return strOut

      End Get

      Set(value As String)

         strOut = value

      End Set

   End Property

End Class

Public MustInherit Class RomanExpression

   Public MustOverride Sub Interpret(value As InputOutput)

End Class

Now, we need a class to format the output of Roman numbers so that it forms the correct ultimate number. Add this class now:

Public Class ParseDecimalToRoman

   Inherits RomanExpression

   Private AllExpressions As New List(Of RomanExpression)() _
      From { _
      New TenThousands(), _
      New Thousands(), _
      New Hundreds(), _
      New Tens(), _
      New Ones() _
   }

   Public Overrides Sub Interpret(value As InputOutput)

      For Each reExp As RomanExpression In AllExpressions

         reExp.Interpret(value)

      Next

   End Sub

End Class

Interpret outputs the information with the help of the InputOutput class. The List that was created contains classes that will make use of the same pattern I have mentioned earlier, as you will see now.

Add a class for multiples of One:

Class Ones

   Inherits Terminals

   Public Overrides Function OneStrings() As String

      Return "I"

   End Function

   Public Overrides Function FourStrings() As String

      Return "IV"

   End Function

   Public Overrides Function FiveStrings() As String

      Return "V"

   End Function

   Public Overrides Function NineStrings() As String

      Return "IX"

   End Function

   Public Overrides Function Multiply() As Integer

      Return 1

   End Function

End Class

This is very, extremely basic. Object-oriented programming 101. All the properties simply output the necessary string when a change should occur. I will explain the Terminals (which this class inherits from) class a bit later. Add the other classes:

Tens

Class Tens

   Inherits Terminals

   Public Overrides Function OneStrings() As String

      Return "X"

   End Function

   Public Overrides Function FourStrings() As String

      Return "XL"

   End Function

   Public Overrides Function FiveStrings() As String

      Return "L"

   End Function

   Public Overrides Function NineStrings() As String

      Return "XC"

   End Function

   Public Overrides Function Multiply() As Integer

      Return 10

   End Function

End Class

Hundreds

Class Hundreds

   Inherits Terminals

   Public Overrides Function OneStrings() As String

      Return "C"

   End Function

   Public Overrides Function FourStrings() As String

      Return "CD"

   End Function

   Public Overrides Function FiveStrings() As String

      Return "D"

   End Function

   Public Overrides Function NineStrings() As String

      Return "CM"

   End Function

   Public Overrides Function Multiply() As Integer

      Return 100

   End Function

End Class

Thousands

Class Thousands

   Inherits Terminals

   Public Overrides Function OneStrings() As String

      Return "M"

   End Function

   Public Overrides Function FourStrings() As String

      Return "MV"

   End Function

   Public Overrides Function FiveStrings() As String

      Return "V"

   End Function

   Public Overrides Function NineStrings() As String

      Return "MX"

   End Function

   Public Overrides Function Multiply() As Integer

      Return 1000

   End Function

End Class

Ten Thousands

Class TenThousands

   Inherits Terminals

   Public Overrides Function OneStrings() As String

      Return "X"

   End Function

   Public Overrides Function FourStrings() As String

      Return "XL"

   End Function

   Public Overrides Function FiveStrings() As String

      Return "L"

   End Function

   Public Overrides Function NineStrings() As String

      Return "XC"

   End Function

   Public Overrides Function Multiply() As Integer

      Return 10000

   End Function

End Class

I'll stop here, but the same pattern applies to hundred thousands and millions.

Putting It All Together

Add the following class:

Public MustInherit Class Terminals

   Inherits RomanExpression

   Public Overrides Sub Interpret(value As InputOutput)

      While value.Input - 9 * Multiply() >= 0

         value.Output += NineStrings()

         value.Input -= 9 * Multiply()

      End While

      While value.Input - 5 * Multiply() >= 0

         value.Output += FiveStrings()

         value.Input -= 5 * Multiply()

      End While

      While value.Input - 4 * Multiply() >= 0

         value.Output += FourStrings()

         value.Input -= 4 * Multiply()

      End While

      While value.Input - Multiply() >= 0

         value.Output += OneStrings()

         value.Input -= Multiply()

      End While

   End Sub

   Public MustOverride Function OneStrings() As String
   Public MustOverride Function FourStrings() As String
   Public MustOverride Function FiveStrings() As String
   Public MustOverride Function NineStrings() As String
   Public MustOverride Function Multiply() As Integer
End Class

This is where all the magic happens. It simply determines if the current value minus one of our pattern values multiplied by one, ten, hundred, thousand, and ten thousand. If it is, it will add the correct Roman number to the end of the string.

Add the following code behind the button you have added to Form 1 earlier:

Public Class Form1

   Private Sub Button3_Click(sender As Object, e As EventArgs) _
      Handles Button3.Click

      For i As Integer = 1 To 9999

         Dim io As New InputOutput(i)

         Dim pdrParse = New ParseDecimalToRoman()

         pdrParse.Interpret(io)

         Console.WriteLine("{0}" & vbTab & "{1}", i, io.Output)


      Next

   End Sub

End Class

I want to show the Roman Numbers up to 9999 into the Output window.

Conclusion

It is amazing how something this—should I say—trivial can end up having so much logic and work involved. Don't you just love programming?



About the Author

Hannes DuPreez

Hannes du Preez is a Microsoft MVP for Visual Basic for the ninth consecutive year. He loves technology and loves Visual Basic. He loves writing articles and proving that Visual Basic is more powerful than what most believe. His ultimate dream is to write a Visual Basic book, hopefully one day that dream will come true. You are most welcome to reach him at: ojdupreez1978@gmail.com

Related Articles

Comments

  • Mr

    Posted by Kevin on 09/20/2016 07:38am

    Yeah, this is nice, I converted to C# so that I could run it on dotnet core. Worked well. It's a pity that Visual Basic has been left behind, oh well.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date