Saturday, March 8, 2008

Roman Numerals, Part 1

This weekend we're going to write a function to convert an integer into a Roman numeral, because storing Roman numerals in a database as strings is not cool.


ASP

  1. function roman(ByVal arabic)
  2.     Dim ones
  3.     Dim tens
  4.     Dim hundreds
  5.     Dim thousands
  6.     ones = Array("", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX")
  7.     tens = Array("", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC")
  8.     hundreds = Array("", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM")
  9.     thousands = Array("", "M", "MM", "MMM", "MMMM")
  10.     if arabic > 4999 then
  11.         ' For large numbers (five thousand and above), a bar is placed above a base numeral to indicate multiplication by 1000.
  12.         ' Since it is not possible to illustrate this in plain ASCII, this function will refuse to convert numbers above 4999.
  13.         Err.Clear
  14.         Err.Raise 9
  15.     elseif arabic = 0 then
  16.         ' About 725, Bede or one of his colleagues used the letter N, the initial of nullae,
  17.         ' in a table of epacts, all written in Roman numerals, to indicate zero.
  18.         roman = "N"

  19.     else
  20.         roman = thousands((arabic - (arabic mod 1000)) / 1000)
  21.         arabic = arabic mod 1000
  22.         roman = roman & hundreds((arabic - (arabic mod 100)) / 100)
  23.         arabic = arabic mod 100
  24.         roman = roman & tens((arabic - (arabic mod 10)) / 10)
  25.         arabic = arabic mod 10
  26.         roman = roman & ones((arabic - (arabic mod 1)) / 1)
  27.         arabic = arabic mod 1
  28.     end if
  29. end function

PHP

  1. function roman($arabic)
  2. {
  3.     $ones = Array("", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX");
  4.     $tens = Array("", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC");
  5.     $hundreds = Array("", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM");
  6.     $thousands = Array("", "M", "MM", "MMM", "MMMM");
  7.     if ($arabic > 4999)
  8.     {
  9.         // For large numbers (five thousand and above), a bar is placed above a base numeral to indicate multiplication by 1000.
  10.         // Since it is not possible to illustrate this in plain ASCII, this function will refuse to convert numbers above 4999.
  11.         die("Cannot represent numbers larger than 4999 in plain ASCII.");
  12.     }
  13.     elseif ($arabic == 0)
  14.     {
  15.         // About 725, Bede or one of his colleagues used the letter N, the initial of nullae,
  16.         // in a table of epacts, all written in Roman numerals, to indicate zero.
  17.         return "N";
  18.     }
  19.     else
  20.     {
  21.         $roman = $thousands[($arabic - fmod($arabic, 1000)) / 1000];
  22.         $arabic = fmod($arabic, 1000);
  23.         $roman .= $hundreds[($arabic - fmod($arabic, 100)) / 100];
  24.         $arabic = fmod($arabic, 100);
  25.         $roman .= $tens[($arabic - fmod($arabic, 10)) / 10];
  26.         $arabic = fmod($arabic, 10);
  27.         $roman .= $ones[($arabic - fmod($arabic, 1)) / 1];
  28.         $arabic = fmod($arabic, 1);
  29.         return $roman;
  30.     }
  31. }

You may have noticed in the PHP example that I used the fmod() function instead of the % operator. That was a clue that you might want to add support for floating point integers. You can learn how to represent fractions from the Wikipedia article on Roman numerals. Here's a couple of hints to get you started:


  • You will need another array to hold the numerals for the fractional values.
  • The fractional portion should be handled after the whole number portion has been converted and modularly divided out.
  • However, pre-check the rounding of the fraction before you start the conversion. If it rounds to a whole number, round it off before the conversion to prevent problems.

Next weekend, we're going to write some utility functions that will be useful for turning Roman numerals back into integers, or if you're feeling particularly crazy, performing mathematical calculations on Roman numerals. The weekend after that is Easter weekend, so we'll write a function to calculate Easter and the related holidays surrounding it.

1 comment:

Keith Alexander W. said...

I like your Roman Numeral library. I needed a function to test for Roman numerals, so I wrote this one:

// Check to see if the string is a Roman Numeral
// NOTE: this doesn't check for fractions, overbars, the Bede "N" (zero) etc.
// NOTE: It also doesn't check for a well-formed Roman Numeral.
function is_roman_numeral( $roman )
{
// Strip every non-word character
// - A-Z, 0-9, apostrophe and understcore are what's left
$roman = preg_replace( "/[^A-Z0-9_']/iu", "", $roman );

// if it contains anything other than MDCLXVI, then it's not a Roman Numeral
$result = preg_match( "/[^MDCLXVI]/u", $roman );

if( $result )
{
return FALSE;
}

return TRUE;
}


Who knows if blogger is going to show it properly. If not, just contact me and I'll send it you in email or something.

Anyway, it's something I wrote in 5 minutes. If you want to add it to your library, modified or otherwise, please feel free.