Roman numerals/Decode

From Rosetta Code
Revision as of 02:19, 30 April 2013 by rosettacode>Gerard Schildberger (→‎version 3: added verbage in the REXX section header. -- ~~~~)
Task
Roman numerals/Decode
You are encouraged to solve this task according to the task description, using any language you may know.

Create a function that takes a Roman numeral as its argument and returns its value as a numeric decimal integer. You don't need to validate the form of the Roman numeral.

Modern Roman numerals are written by expressing each decimal digit of the number to be encoded separately, starting with the leftmost digit and skipping any 0s. So 1990 is rendered "MCMXC" (1000 = M, 900 = CM, 90 = XC) and 2008 is rendered "MMVIII" (2000 = MM, 8 = VIII). The Roman numeral for 1666, "MDCLXVI", uses each letter in descending order.

Ada

Translation of: Delphi

<lang Ada>with Ada.Text_IO;

procedure Decode_Roman_Numerals is

  function Roman_To_Integer(A_Roman: String) return Integer is
     function Decode_Roman_Digit (A_Char: Character) return Integer is
     begin
        case A_Char is
           when 'M' | 'm' => return 1000;
           when 'D' | 'd' => return 500;
           when 'C' | 'c' => return 100;
           when 'L' | 'l' => return 50;
           when 'X' | 'x' => return 10;
           when 'V' | 'v' => return 5;
           when 'I' | 'i' => return  1;
           when others => return 0;
        end case;
     end Decode_Roman_Digit;
     L_Curr_Val: Integer;
     L_Last_Val: Integer;
     Result: Integer;
  begin
     Result := 0;
     L_Last_Val := 0;
     for i in reverse 1 .. A_Roman'Length loop
        L_Curr_Val := Decode_Roman_Digit(A_Roman(I));
        if L_Curr_Val < L_Last_Val then
           Result := Result - L_Curr_Val;
        else
           Result := Result + L_Curr_Val;
        end if;
        L_Last_Val := L_Curr_Val;
     end loop;
     return Result;
  end Roman_To_Integer;

begin

  Ada.Text_IO.Put_Line(Integer'Image(Roman_To_Integer("MCMXC")));    -- 1990
  Ada.Text_IO.Put_Line(Integer'Image(Roman_To_Integer("MMVIII")));   -- 2008
  Ada.Text_IO.Put_Line(Integer'Image(Roman_To_Integer("MDCLXVI")));  -- 1666

end Decode_Roman_Numerals;</lang>

ALGOL 68

Works with: ALGOL 68G version Any - tested with release 2.2.0

Note: roman to int will handle multiple subtraction, e.g. IIIIX for 6. <lang Algol68> PROC roman to int = (STRING roman) INT:

   BEGIN
       PROC roman digit value = (CHAR roman digit) INT:
           (roman digit = "M" | 1000 |:
            roman digit = "D" |  500 |:
            roman digit = "C" |  100 |:
            roman digit = "L" |   50 |:
            roman digit = "X" |   10 |:
            roman digit = "V" |    5 |:
            roman digit = "I" |    1);
       INT result := 0, previous value := 0, run := 0;
       
       FOR i FROM LWB roman TO UPB roman
       DO
           INT value = roman digit value(roman[i]);
           IF previous value = value THEN
               run +:= value
           ELSE
               IF previous value < value THEN
                   result -:= run
               ELSE
                   result +:= run
               FI;
               run := previous value := value
           FI
       OD;
       
       result +:= run
   END;
   MODE TEST = STRUCT (STRING input, INT expected output);
   
   [] TEST roman test = (
       ("MMXI",    2011), ("MIM",     1999),
       ("MCMLVI",  1956), ("MDCLXVI", 1666),
       ("XXCIII",    83), ("LXXIIX",    78),
       ("IIIIX",      6)
   );
   
   print(("Test input  Value   Got", newline, "--------------------------", newline));
   FOR i FROM LWB roman test TO UPB roman test
   DO
       INT output = roman to int(input OF roman test[i]);
       printf(($g, n (12 - UPB input OF roman test[i]) x$, input OF roman test[i]));
       printf(($g(5), 1x, g(5), 1x$, expected output OF roman test[i], output));
       printf(($b("ok", "not ok"), 1l$, output = expected output OF roman test[i]))
   OD</lang>

ANTLR

Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral


Java

<lang java>/* Parse Roman Numerals

  Nigel Galloway March 16th., 2012
  • /

grammar ParseRN ;

options { language = Java; } @members { int rnValue; int ONE; }

parseRN: ({rnValue = 0;} rn NEWLINE {System.out.println($rn.text + " = " + rnValue);})* ;

rn : (Thousand {rnValue += 1000;})* hundreds? tens? units?;

hundreds: {ONE = 0;} (h9 | h5) {if (ONE > 3) System.out.println ("Too many hundreds");}; h9 : Hundred {ONE += 1;} (FiveHund {rnValue += 400;}| Thousand {rnValue += 900;}|{rnValue += 100;} (Hundred {rnValue += 100; ONE += 1;})*); h5 : FiveHund {rnValue += 500;} (Hundred {rnValue += 100; ONE += 1;})*;

tens : {ONE = 0;} (t9 | t5) {if (ONE > 3) System.out.println ("Too many tens");}; t9 : Ten {ONE += 1;} (Fifty {rnValue += 40;}| Hundred {rnValue += 90;}|{rnValue += 10;} (Ten {rnValue += 10; ONE += 1;})*); t5 : Fifty {rnValue += 50;} (Ten {rnValue += 10; ONE += 1;})*;

units : {ONE = 0;} (u9 | u5) {if (ONE > 3) System.out.println ("Too many ones");}; u9 : One {ONE += 1;} (Five {rnValue += 4;}| Ten {rnValue += 9;}|{rnValue += 1;} (One {rnValue += 1; ONE += 1;})*); u5 : Five {rnValue += 5;} (One {rnValue += 1; ONE += 1;})*;

One : 'I'; Five : 'V'; Ten : 'X'; Fifty : 'L'; Hundred: 'C'; FiveHund: 'D'; Thousand: 'M' ; NEWLINE: '\r'? '\n' ;</lang> Using this test data:

MMXI
MCMLVI
XXCIII
MCMXC
MMVIII
MDCLXVI
IIIIX
MIM
MDCLXVI
LXXIIX
M
MCXI
CMXI
MCM
MMIX
MCDXLIV
MMXII

Produces:

MMXI = 2011
MCMLVI = 1956
line 3:2 missing NEWLINE at 'C'
XX = 20
CIII = 103

Note that this implementation does not accept XXC as eighty. The error is detected and ANTLR attempts to continue by inserting the expected NEWLINE after XX and treating CIII as a new Number.

MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666
Too many ones
line 7:4 extraneous input 'X' expecting NEWLINE
IIII = 4

An implementation above thinks IIIIX is 6. It isn't. ANTLR detects the surfiet of 'I' reports the errors and tries to carry on.

line 8:2 no viable alternative at input 'M'
MIM = 1000
MDCLXVI = 1666
line 10:5 extraneous input 'X' expecting NEWLINE
LXXII = 72
M = 1000
MCXI = 1111
CMXI = 911
MCM = 1900
MMIX = 2009
MCDXLIV = 1444
MMXII = 2012

AutoHotkey

Works with: AutoHotkey_L

<lang AHK>Roman_Decode(str){ res := 0 Loop Parse, str { n := {M: 1000, D:500, C:100, L:50, X:10, V:5, I:1}[A_LoopField] If ( n > OldN ) && OldN res -= 2*OldN res += n, oldN := n } return res }

test = MCMXC|MMVIII|MDCLXVI Loop Parse, test, |

  res .= A_LoopField "`t= " Roman_Decode(A_LoopField) "`r`n"

clipboard := res</lang>

Output:
MCMXC	= 1990
MMVIII	= 2008
MDCLXVI	= 1666

AWK

<lang AWK># syntax: GAWK -f ROMAN_NUMERALS_DECODE.AWK BEGIN {

   leng = split("MCMXC MMVIII MDCLXVI",arr," ")
   for (i=1; i<=leng; i++) {
     n = arr[i]
     printf("%s = %s\n",n,roman2arabic(n))
   }
   exit(0)

} function roman2arabic(r, a,i,p,q,u,ua,una,unr) {

   r = toupper(r)
   unr = "MDCLXVI" # each Roman numeral in descending order
   una = "1000 500 100 50 10 5 1" # and its Arabic equivalent
   split(una,ua," ")
   i = split(r,u,"")
   a = ua[index(unr,u[i])]
   while (--i) {
     p = index(unr,u[i])
     q = index(unr,u[i+1])
     a += ua[p] * ((p>q) ? -1 : 1)
   }
   return( (a>0) ? a : "" )

}</lang>

Output:
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666

BBC BASIC

<lang bbcbasic> PRINT "MCMXCIX", FNromandecode("MCMXCIX")

     PRINT "MMXII", FNromandecode("MMXII")
     PRINT "MDCLXVI", FNromandecode("MDCLXVI")
     PRINT "MMMDCCCLXXXVIII", FNromandecode("MMMDCCCLXXXVIII")
     END
     
     DEF FNromandecode(roman$)
     LOCAL i%, j%, p%, n%, r%()
     DIM r%(7) : r%() = 0,1,5,10,50,100,500,1000
     FOR i% = LEN(roman$) TO 1 STEP -1
       j% = INSTR("IVXLCDM", MID$(roman$,i%,1))
       IF j%=0 ERROR 100, "Invalid character"
       IF j%>=p% n% += r%(j%) ELSE n% -= r%(j%)
       p% = j%
     NEXT
     = n%</lang>

Output:

MCMXCIX         1999
MMXII           2012
MDCLXVI         1666
MMMDCCCLXXXVIII           3888

Bracmat

Translation of: Icon and Unicon

<lang bracmat> ( unroman

 =   nbr,lastVal,val
   .     0:?nbr:?lastVal
       & @( low$!arg
          :   ?
              %@?L
              ( ?
              &     (m.1000)
                    (d.500)
                    (c.100)
                    (l.50)
                    (x.10)
                    (v.5)
                    (i.1)
                : ? (!L.?val) ?
              &     (!val:~>!lastVal|!val+-2*!lastVal)
                  + !nbr
                : ?nbr
              & !val:?lastVal
              & ~
              )
          )
     | !nbr
 )

& (M.1000)

     (MCXI.1111)
     (CMXI.911)
     (MCM.1900)
     (MCMXC.1990)
     (MMVIII.2008)
     (MMIX.2009)
     (MCDXLIV.1444)
     (MDCLXVI.1666)
     (MMXII.2012)
 : ?years

& (test=.out$(!arg unroman$!arg)) & ( !years

   : ? (?L.?D) (?&test$!L&~)
 | done
 );</lang>
Output:
M 1000
MCXI 1111
CMXI 911
MCM 1900
MCMXC 1990
MMVIII 2008
MMIX 2009
MCDXLIV 1444
MDCLXVI 1666
MMXII 2012

C

Note: the code deliberately did not distinguish between "I", "J" or "U", "V", doing what Romans did for fun. <lang C>#include <stdio.h>

int digits[26] = { 0, 0, 100, 500, 0, 0, 0, 0, 1, 1, 0, 50, 1000, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 10, 0, 0 };

/* assuming ASCII, do upper case and get index in alphabet. could also be

       inline int VALUE(char x) { return digits [ (~0x20 & x) - 'A' ]; }
  if you think macros are evil */
  1. define VALUE(x) digits[(~0x20 & (x)) - 'A']

int decode(const char * roman) {

       const char *bigger;
       int current;
       int arabic = 0;
       while (*roman != '\0') {
               current = VALUE(*roman);
               /*      if (!current) return -1;
                       note: -1 can be used as error code; Romans didn't even have zero
               */
               bigger = roman;
               /* look for a larger digit, like IV or XM */
               while (VALUE(*bigger) <= current && *++bigger != '\0');
               if (*bigger == '\0')
                       arabic += current;
               else {
                       arabic += VALUE(*bigger);
                       while (roman < bigger)
                               arabic -= VALUE(* (roman++) );
               }
               roman ++;
       }
       return arabic;

}

int main() {

       const char * romans[] = { "MCmxC", "MMVIII", "MDClXVI", "MCXLUJ" };
       int i;
       for (i = 0; i < 4; i++)
               printf("%s\t%d\n", romans[i], decode(romans[i]));
       return 0;

}</lang>

C++

<lang cpp>#include <iostream>

  1. include <string>
  2. include <algorithm>
  3. include <map>

static std::map<char , int> Romans;

int RomanToInt( const std::string &roman ) {

  int number = 0 ;
  // look for all the letters in the array
  for ( std::string::const_iterator i = roman.begin( ) ; i != roman.end( ) ; i++ ) {
     std::map<char , int>::const_iterator it1 = Romans.find( *i ) ;
     if ( it1 == Romans.end( ) ) {
        std::cerr << *i << " not a valid Roman numeral character." << std::endl ;
        return -1 ;
     }
     int pos1 = it1->second ;
     if ( i + 1 != roman.end( ) ) {
        std::map<char , int>::const_iterator it2 = Romans.find( *( i + 1 ) );
        if ( it2 == Romans.end( ) ) {
           std::cerr << *( i + 1 ) << " not a valid Roman numeral character." << std::endl ;
           return -1 ;
        }
        int pos2 = it2->second ;
        if ( pos2 > pos1 ) {
           number += pos2 - pos1 ;
           i++ ;
           continue ;
        }
     }
     number += pos1 ;
  }
  return number ;

}

int main( ) {

  Romans[ 'I' ] = 1 ;
  Romans[ 'V' ] = 5 ;
  Romans[ 'X' ] = 10 ;
  Romans[ 'L' ] = 50 ;
  Romans[ 'C' ] = 100 ;
  Romans[ 'D' ] = 500 ;
  Romans[ 'M' ] = 1000 ;
  std::cout << "MCMXC in Roman numerals is " << RomanToInt( "MCMXC" ) << " in Arabic!\n" ;
  std::cout << "And MDCLXVI for the Romans is " << RomanToInt( "MDCLXVI" ) << " in better known Arabic figures!\n" ;
  return 0 ;

}</lang>

Output:
MCMXC in Roman numerals is 1990 in Arabic!
And MDCLXVI for the Romans is 1666 in better known Arabic figures!

C#

<lang csharp>using System; using System.Collections.Generic;

namespace Roman {

   internal class Program
   {
       private static void Main(string[] args)
       {
           // Decode and print the numerals.
           Console.WriteLine("{0}: {1}", "MCMXC", Decode("MCMXC"));
           Console.WriteLine("{0}: {1}", "MMVIII", Decode("MMVIII"));
           Console.WriteLine("{0}: {1}", "MDCLXVI", Decode("MDCLXVI"));
       }
       // Dictionary to hold our numerals and their values.
       private static readonly Dictionary<char, int> RomanDictionary = new Dictionary<char, int>
                                                                           {
                                                                               {'I', 1},
                                                                               {'V', 5},
                                                                               {'X', 10},
                                                                               {'L', 50},
                                                                               {'C', 100},
                                                                               {'D', 500},
                                                                               {'M', 1000}
                                                                           };
       private static int Decode(string roman)
       {
           /* Make the input string upper-case,
            * because the dictionary doesn't support lower-case characters. */
           roman = roman.ToUpper();
           /* total = the current total value that will be returned.
            * minus = value to subtract from next numeral. */
           int total = 0, minus = 0;
           for (int i = 0; i < roman.Length; i++) // Iterate through characters.
           {
               // Get the value for the current numeral. Takes subtraction into account.
               int thisNumeral = RomanDictionary[roman[i]] - minus;
               /* Checks if this is the last character in the string, or if the current numeral
                * is greater than or equal to the next numeral. If so, we will reset our minus
                * variable and add the current numeral to the total value. Otherwise, we will
                * subtract the current numeral from the next numeral, and continue. */
               if (i >= roman.Length - 1 ||
                   thisNumeral + minus >= RomanDictionary[roman[i + 1]])
               {
                   total += thisNumeral;
                   minus = 0;
               }
               else
               {
                   minus = thisNumeral;
               }
           }
           return total; // Return the total.
       }
   }

}</lang>

Output:
MCMXC: 1990
MMVIII: 2008
MDCLXVI: 1666

Clojure

<lang clojure>(defn ro2ar [r]

 (->> (reverse r)
      (replace (zipmap "MDCLXVI" [1000 500 100 50 10 5 1]))
      (partition-by identity)
      (map (partial apply +))
      (reduce #(if (< %1 %2) (+ %1 %2) (- %1 %2)))))</lang>
Output:
(map ro2ar ["MDCLXVI" "MMMCMXCIX" "XLVIII" "MMVIII"])
(1666 3999 48 2008)

CoffeeScript

<lang coffeescript>roman_to_demical = (s) ->

 # s is well-formed Roman Numeral >= I
 numbers =
   M: 1000
   D: 500
   C: 100
   L: 50
   X: 10
   V: 5
   I: 1
 result = 0
 for c in s
   num = numbers[c]
   result += num
   if old_num < num
     # If old_num exists and is less than num, then
     # we need to subtract it twice, once because we
     # have already added it on the last pass, and twice
     # to conform to the Roman convention that XC = 90,
     # not 110.
     result -= 2 * old_num
   old_num = num
 result
 

tests =

 IV: 4
 XLII: 42
 MCMXC: 1990
 MMVIII: 2008
 MDCLXVI: 1666

for roman, expected of tests

 dec = roman_to_demical(roman)
 console.log "error" if dec != expected
 console.log "#{roman} = #{dec}"</lang>

Common Lisp

Doesn't have much in error checking: <lang lisp>(defun parse-roman (r)

 (loop for l on (map 'list (lambda (c)

(getf '(#\I 1 #\V 5 #\X 10 #\L 50 #\C 100 #\D 500 #\M 1000) c)) (string-upcase r))

    sum (let ((a (first l)) (b (second l)))

(if (and b (< a b)) (- a) a))))

test code

(dolist (r '("MCMXC" "MDCLXVI" "MMVIII"))

 (format t "~a: ~d~%" r (parse-roman r)))</lang>
Output:
MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008

D

<lang d>import std.regex, std.algorithm;

int toArabic(in string s) /*pure nothrow*/ {

   static immutable weights = [1000, 900, 500, 400, 100,
                               90, 50, 40, 10, 9, 5, 4, 1];
   static immutable symbols = ["M","CM","D","CD","C","XC",
                               "L","XL","X","IX","V","IV","I"];
   int arabic;
   foreach (m; s.match(regex(r"CM|CD|XC|XL|IX|IV|[MDCLXVI]", "g")))
       arabic += weights[symbols.countUntil(m.hit)];
   return arabic;

}

void main() {

   assert(toArabic("MCMXC") == 1990);
   assert(toArabic("MMVIII") == 2008);
   assert(toArabic("MDCLXVI") == 1666);

}</lang> Alternative version: <lang d>import std.regex, std.algorithm;

immutable int[string] w2s;

nothrow static this() {

   w2s = ["IX":  9, "C":  100, "D":  500, "CM": 900, "I":   1,
          "XC": 90, "M": 1000, "L":   50, "CD": 400, "XL": 40,
          "V":   5, "X":   10, "IV":   4];

}

int toArabic(in string s) /*pure nothrow*/ {

   auto ms = match(s, regex("CM|CD|XC|XL|IX|IV|[MDCLXVI]", "g"));
   return reduce!((a, m) => a + w2s[m.hit])(0, ms);

}

void main() {

   assert("MCMXC".toArabic == 1990);
   assert("MMVIII".toArabic == 2008);
   assert("MDCLXVI".toArabic == 1666);

}</lang>

Delphi/Pascal

<lang delphi>program RomanNumeralsDecode;

{$APPTYPE CONSOLE}

function RomanToInteger(const aRoman: string): Integer;

 function DecodeRomanDigit(aChar: Char): Integer;
 begin
   case aChar of
     'M', 'm': Result := 1000;
     'D', 'd': Result := 500;
     'C', 'c': Result := 100;
     'L', 'l': Result := 50;
     'X', 'x': Result := 10;
     'V', 'v': Result := 5;
     'I', 'i': Result := 1
   else
     Result := 0;
   end;
 end;

var

 i: Integer;
 lCurrVal: Integer;
 lLastVal: Integer;

begin

 Result := 0;
 lLastVal := 0;
 for i := Length(aRoman) downto 1 do
 begin
   lCurrVal := DecodeRomanDigit(aRoman[i]);
   if lCurrVal < lLastVal then
     Result := Result - lCurrVal
   else
     Result := Result + lCurrVal;
   lLastVal := lCurrVal;
 end;

end;

begin

 Writeln(RomanToInteger('MCMXC'));    // 1990
 Writeln(RomanToInteger('MMVIII'));   // 2008
 Writeln(RomanToInteger('MDCLXVI'));  // 1666

end.</lang>

Emacs Lisp

<lang lisp> (defun ro2ar (RN)

 "translate a roman number RN into arabic number. 
  Its argument RN is wether a symbol, wether a list. 
  Returns the arabic number. (ro2ar 'C) gives 100, 
  (ro2ar '(X X I V)) gives 24" 
 (cond 
  ((eq RN 'M) 1000)
  ((eq RN 'D) 500)
  ((eq RN 'C) 100)
  ((eq RN 'L) 50)
  ((eq RN 'X) 10)
  ((eq RN 'V) 5)
  ((eq RN 'I) 1)
  ((null (cdr RN)) (ro2ar (car RN))) ;; stop recursion
  ((< (ro2ar (car RN)) (ro2ar (car (cdr RN)))) (- (ro2ar (cdr RN)) (ro2ar (car RN)))) ;; "IV" -> 5-1=4
  (t (+ (ro2ar (car RN)) (ro2ar (cdr RN)))))) ;; "VI" -> 5+1=6

</lang>

Output:
(ro2ar '(M D C L X V I)) -> 1666

Euphoria

Translation of: PureBasic

<lang euphoria>constant symbols = "MDCLXVI", weights = {1000,500,100,50,10,5,1} function romanDec(sequence roman)

   integer n, lastval, arabic
   lastval = 0
   arabic = 0
   for i = length(roman) to 1 by -1 do
       n = find(roman[i],symbols)
       if n then
           n = weights[n]
       end if
       if n < lastval then
           arabic -= n
       else
           arabic += n
       end if
       lastval = n
   end for
   return arabic

end function

? romanDec("MCMXCIX") ? romanDec("MDCLXVI") ? romanDec("XXV") ? romanDec("CMLIV") ? romanDec("MMXI")</lang>

Output:
1999
1666
25
954
2011

F#

This implementation uses tail recursion. The accumulator (arabic) and the last roman digit (lastval) are recursively passed as each element of the list is consumed. <lang fsharp>let decimal_of_roman roman =

   let rec convert arabic lastval = function
       | head::tail ->
           let n = match head with
                   | 'M' | 'm' -> 1000
                   | 'D' | 'd' -> 500
                   | 'C' | 'c' -> 100
                   | 'L' | 'l' -> 50
                   | 'X' | 'x' -> 10
                   | 'V' | 'v' -> 5
                   | 'I' | 'i' -> 1
                   | _ -> 0
           let op = if n > lastval then (-) else (+)
           convert (op arabic lastval) n tail
       | _ -> arabic + lastval
   convert 0 0 (Seq.toList roman)
</lang>

Here is an alternative implementation that uses Seq(uence).fold. It threads a Tuple of the state (accumulator, last roman digit) through the list of characters. <lang fsharp>let decimal_of_roman roman =

   let convert (arabic,lastval) c =
       let n = match c with
               | 'M' | 'm' -> 1000
               | 'D' | 'd' -> 500
               | 'C' | 'c' -> 100
               | 'L' | 'l' -> 50
               | 'X' | 'x' -> 10
               | 'V' | 'v' -> 5
               | 'I' | 'i' -> 1
               | _ -> 0
       let op = if n > lastval then (-) else (+)
       (op arabic lastval, n)
   let (arabic, lastval) = Seq.fold convert (0,0) roman
   arabic + lastval
</lang>

Test code: <lang fsharp>let tests = ["MCMXC"; "MMVIII"; "MDCLXVII"; "MMMCLIX"; "MCMLXXVII"; "MMX"] for test in tests do Printf.printf "%s: %d\n" test (decimal_of_roman test)

</lang>
Output:
MCMXC: 1990
MMVIII: 2008
MDCLXVII: 1667
MMMCLIX: 3159
MCMLXXVII: 1977
MMX: 2010

Forth

<lang forth>create (arabic)

 1000 128 * char M + ,
  500 128 * char D + ,
  100 128 * char C + ,
   50 128 * char L + ,
   10 128 * char X + ,
    5 128 * char V + ,
    1 128 * char I + ,

does>

 7 cells bounds do
   i @ over over 127 and = if nip 7 rshift leave else drop then
 1 cells +loop dup
>arabic
 0 dup >r >r
 begin
   over over
 while
   c@ dup (arabic) rot <>
 while
   r> over r> over over > if 2* negate + else drop then + swap >r >r 1 /string
 repeat then drop 2drop r> r> drop

s" MCMLXXXIV" >arabic .</lang>

Fortran

Works with: Fortran version 90 and later

<lang fortran>program Roman_decode

 implicit none

 write(*,*) decode("MCMXC"), decode("MMVIII"), decode("MDCLXVI")

contains

function decode(roman) result(arabic)

 character(*), intent(in) :: roman
 integer :: i, n, lastval, arabic
 arabic = 0
 lastval = 0
 do i = len(roman), 1, -1
   select case(roman(i:i))
     case ('M','m')
       n = 1000
     case ('D','d')
       n = 500
     case ('C','c')
       n = 100
     case ('L','l')
       n = 50
     case ('X','x')
       n = 10
     case ('V','v')
       n = 5
     case ('I','i')
       n = 1
     case default
       n = 0
   end select
   if (n < lastval) then
     arabic = arabic - n
   else
     arabic = arabic + n
   end if
   lastval = n
 end do

end function decode end program Roman_decode</lang>

Output:
        1990        2008        1666

Go

For fluff, the unicode overbar is recognized as a factor of 1000, as described in WP. <lang go>package main

import (

   "errors"
   "fmt"

)

var m = map[rune]int{

   'I': 1,
   'V': 5,
   'X': 10,
   'L': 50,
   'C': 100,
   'D': 500,
   'M': 1000,

}

func parseRoman(s string) (r int, err error) {

   if s == "" {
       return 0, errors.New("Empty string")
   }
   is := []rune(s) // easier to convert string up front
   var c0 rune     // c0: roman character last read
   var cv0 int     // cv0: value of cv
   // the key to the algorithm is to process digits from right to left
   for i := len(is) - 1; i >= 0; i-- {
       // read roman digit
       c := is[i]
       k := c == 0x305 // unicode overbar combining character
       if k {
           if i == 0 {
               return 0, errors.New(
                   "Overbar combining character invalid at position 0")
           }
           i--
           c = is[i]
       }
       cv := m[c]
       if cv == 0 {
           if c == 0x0305 {
               return 0, errors.New(fmt.Sprintf(
                   "Overbar combining character invalid at position %d", i))
           } else {
               return 0, errors.New(fmt.Sprintf(
                   "Character unrecognized as Roman digit: %c", c))
           }
       }
       if k {
           c = -c // convention indicating overbar
           cv *= 1000
       }
       // handle cases of new, same, subtractive, changed, in that order.
       switch {
       default: // case 4: digit change
           fallthrough
       case c0 == 0: // case 1: no previous digit
           c0 = c
           cv0 = cv
       case c == c0: // case 2: same digit
       case cv*5 == cv0 || cv*10 == cv0: // case 3: subtractive
           // handle next digit as new.
           // a subtractive digit doesn't count as a previous digit.
           c0 = 0
           r -= cv  // subtract...
           continue // ...instead of adding
       }
       r += cv // add, in all cases except subtractive
   }
   return r, nil

}

func main() {

   // parse three numbers mentioned in task description
   for _, r := range []string{"MCMXC", "MMVIII", "MDCLXVI"} {
       v, err := parseRoman(r)
       if err != nil {
           fmt.Println(err)
       } else {
           fmt.Println(r, "==", v)
       }
   }

}</lang>

Output:
MCMXC == 1990
MMVIII == 2008
MDCLXVI == 1666

Simpler: <lang go>package main

import ( "fmt" "strings" )

var m = map[string]int{ "I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000, }

// function, per task description func from_roman(roman string) (arabic int) { last_digit := 1000 for _, r := range strings.Split(roman, "") { digit := m[r] if last_digit < digit { arabic -= 2 * last_digit } last_digit = digit arabic += digit }

return arabic }

func main() { // parse three numbers mentioned in task description for _, roman_digit := range []string{"MCMXC", "MMVIII", "MDCLXVI"} { fmt.Printf("%-10s == %d\n", roman_digit, from_roman(roman_digit)) } }</lang>

Groovy

Solution: <lang groovy>enum RomanDigits {

   I(1), V(5), X(10), L(50), C(100), D(500), M(1000);
   
   private magnitude;
   private RomanDigits(magnitude) { this.magnitude = magnitude }
   
   String toString() { super.toString() + "=${magnitude}" }
   
   static BigInteger parse(String numeral) {
       assert numeral != null && !numeral.empty
       def digits = (numeral as List).collect {
           RomanDigits.valueOf(it)
       }
       def L = digits.size()
       (0..<L).inject(0g) { total, i ->
           def sign = (i == L - 1 || digits[i] >= digits[i+1]) ? 1 : -1
           total + sign * digits[i].magnitude
       }
   }

}</lang> Test: <lang groovy>println """ Digit Values = ${RomanDigits.values()} M => ${RomanDigits.parse('M')} MCXI => ${RomanDigits.parse('MCXI')} CMXI => ${RomanDigits.parse('CMXI')} MCM => ${RomanDigits.parse('MCM')} MCMXC => ${RomanDigits.parse('MCMXC')} MMVIII => ${RomanDigits.parse('MMVIII')} MMIX => ${RomanDigits.parse('MMIX')} MCDXLIV => ${RomanDigits.parse('MCDXLIV')} MDCLXVI => ${RomanDigits.parse('MDCLXVI')} """</lang>

Output:
Digit Values = [I=1, V=5, X=10, L=50, C=100, D=500, M=1000]
M       => 1000
MCXI    => 1111
CMXI    => 911
MCM     => 1900
MCMXC   => 1990
MMVIII  => 2008
MMIX    => 2009
MCDXLIV => 1444
MDCLXVI => 1666

Haskell

<lang Haskell>import Data.List (isPrefixOf)

mapping = [("M",1000),("CM",900),("D",500),("CD",400),("C",100),("XC",90),

          ("L",50),("XL",40),("X",10),("IX",9),("V",5),("IV",4),("I",1)]

toArabic :: String -> Int toArabic "" = 0 toArabic str = num + toArabic xs

   where (num, xs):_ = [ (num, drop (length n) str) | (n,num) <- mapping, isPrefixOf n str ]</lang>

Usage:

ghci> toArabic "MCMXC"
1990
ghci> toArabic "MMVIII"
2008
ghci> toArabic "MDCLXVI"
1666

Icon and Unicon

<lang Icon>link numbers

procedure main() every R := "MCMXC"|"MDCLXVI"|"MMVIII" do

  write(R, " = ",unroman(R))

end</lang>

numbers.icn provides unroman

The code for this procedure is copied below: <lang Icon>procedure unroman(s) #: convert Roman numeral to integer

  local nbr,lastVal,val
  nbr := lastVal := 0
  s ? {
     while val := case map(move(1)) of {

"m": 1000 "d": 500 "c": 100 "l": 50 "x": 10 "v": 5 "i": 1 } do { nbr +:= if val <= lastVal then val else val - 2 * lastVal lastVal := val }

     }
  return nbr

end</lang>

Output:
MCMXC = 1990
MDCLXVI = 1666
MMVIII = 2008

J

<lang j>rom2d=: [: (+/ .* _1^ 0,~ 2</\ ]) 1 5 10 50 100 500 1000 {~ 'IVXLCDM'&i.</lang> Example use: <lang j> rom2d 'MCMXC' 1990

  rom2d 'MDCLXVI'

1666

  rom2d 'MMVIII'

2008</lang>

Java

<lang java>public class Roman { private static int decodeSingle(char letter) { switch(letter) { case 'M': return 1000; case 'D': return 500; case 'C': return 100; case 'L': return 50; case 'X': return 10; case 'V': return 5; case 'I': return 1; default: return 0; } } public static int decode(String roman) { int result = 0; String uRoman = roman.toUpperCase(); //case-insensitive for(int i = 0;i < uRoman.length() - 1;i++) {//loop over all but the last character //if this character has a lower value than the next character if (decodeSingle(uRoman.charAt(i)) < decodeSingle(uRoman.charAt(i+1))) { //subtract it result -= decodeSingle(uRoman.charAt(i)); } else { //add it result += decodeSingle(uRoman.charAt(i)); } } //decode the last character, which is always added result += decodeSingle(uRoman.charAt(uRoman.length()-1)); return result; }

public static void main(String[] args) { System.out.println(decode("MCMXC")); //1990 System.out.println(decode("MMVIII")); //2008 System.out.println(decode("MDCLXVI")); //1666 } }</lang>

Output:
1990
2008
1666

JavaScript

Works with: Rhino
Works with: SpiderMonkey

<lang javascript>var Roman = {

 Values: [['M', 1000], ['CM', 900], ['D',  500], ['CD', 400], 
          ['C',  100], ['XC',  90], ['L',  50],  ['XL',  40],  
          ['X',   10], ['IX',   9], ['V',   5],  ['IV',   4],   
          ['I',    1]],
 parse: function(str) {
   var result = 0
   for (var i=0; i<Roman.Values.length; ++i) {
     var pair = Roman.Values[i]
     var key = pair[0]
     var value = pair[1]
     var regex = RegExp('^' + key)
     while (str.match(regex)) {
       result += value
       str = str.replace(regex, )
     }
   }
   return result
 }

}

var test_data = ['MCMXC', 'MDCLXVI', 'MMVIII'] for (var i=0; i<test_data.length; ++i) {

 var test_datum = test_data[i]
 print(test_datum + ": " + Roman.parse(test_datum)) 

}</lang>

Output:
MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008

K

Translation of: J

<lang k> romd: {v:1 5 10 50 100 500 1000@"IVXLCDM"?/:x; +/v*_-1^(>':v),0}</lang> Example: <lang k> romd'("MCMXC";"MMVIII";"MDCLXVI") 1990 2008 1666</lang>

Liberty BASIC

As Fortran & PureBasic. <lang lb> print "MCMXCIX = "; romanDec( "MCMXCIX") '1999

 print "MDCLXVI = "; romanDec( "MDCLXVI") '1666
 print "XXV     = "; romanDec( "XXV")     '25
 print "CMLIV   = "; romanDec( "CMLIV")   '954
 print "MMXI    = "; romanDec( "MMXI")    '2011
 end

function romanDec( roman$)

 arabic  =0
 lastval =0
 for i = len( roman$) to 1 step -1
   select case upper$( mid$( roman$, i, 1))
     case "M"
       n = 1000
     case "D"
       n = 500
     case "C"
       n = 100
     case "L"
       n = 50
     case "X"
       n = 10
     case "V"
       n = 5
     case "I"
       n = 1
     case else
       n = 0
   end select
   if n <lastval then
     arabic =arabic -n
   else
     arabic =arabic +n
   end if
   lastval =n
 next
 romanDec =arabic

end function</lang>

MCMXCIX = 1999
MDCLXVI = 1666
XXV     = 25
CMLIV   = 954
MMXI    = 2011

<lang logo>; Roman numeral decoder

First, some useful substring utilities

to starts_with? :string :prefix

 if empty? :prefix [output "true]
 if empty? :string [output "false]
 if not equal? first :string first :prefix [output "false]
 output starts_with? butfirst :string butfirst :prefix

end

to remove_prefix :string :prefix

 if or empty? :prefix not starts_with? :string :prefix [output :string]
 output remove_prefix butfirst :string butfirst :prefix

end

Our list of Roman numeral values

make "values [[M 1000] [CM 900] [D 500] [CD 400] [C 100] [XC 90] [L 50]

             [XL 40]  [X  10]  [IX 9]   [V  5]   [IV 4]   [I  1]]
Function to do the work

to from_roman :str

local "n make "n 0
foreach :values [
  local "s make "s first ?
  local "v make "v last ?
  while [starts_with? :str :s] [
    make "n sum n :v
    make "str remove_prefix :str :s
  ]
]
output :n

end

foreach [MCMXC MDCLXVI MMVIII] [print (sentence (word ? "|: |) from_roman ?)] bye</lang>

Output:
MCMXC:  1990
MDCLXVI:  1666
MMVIII:  2008

Lua

<lang lua>function ToNumeral( roman )

   local Num = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
   local numeral = 0    
   
   local i = 1
   local strlen = string.len(roman)
   while i < strlen do
       local z1, z2 = Num[ string.sub(roman,i,i) ], Num[ string.sub(roman,i+1,i+1) ]
       if z1 < z2 then
           numeral = numeral + ( z2 - z1 )
           i = i + 2
       else
           numeral = numeral + z1
           i = i + 1    
       end        
   end
   
   if i <= strlen then numeral = numeral + Num[ string.sub(roman,i,i) ] end
   
   return numeral    

end

print( ToNumeral( "MCMXC" ) ) print( ToNumeral( "MMVIII" ) ) print( ToNumeral( "MDCLXVI" ) )</lang>

1990
2008
1666

NetRexx

<lang NetRexx>/* NetRexx */ options replace format comments java crossref savelog symbols binary

          /* 1990  2008   1666    */

years = Rexx('MCMXC MMVIII MDCLXVI')

loop y_ = 1 to years.words

   Say years.word(y_).right(10) || ':' decode(years.word(y_))
   end y_

return

method decode(arg) public static returns int signals IllegalArgumentException

 parse arg.upper roman .
 if roman.verify('MDCLXVI') \= 0 then signal IllegalArgumentException
 -- always insert the value of the least significant numeral
 decnum = rchar(roman.substr(roman.length, 1))
 loop d_ = 1 to roman.length - 1
   if rchar(roman.substr(d_, 1)) < rchar(roman.substr(d_ + 1, 1)) then do
     -- Handle cases where numerals are not in descending order
     --   subtract the value of the numeral
     decnum = decnum - rchar(roman.substr(d_, 1))
     end
   else do
     -- Normal case
     --   add the value of the numeral
     decnum = decnum + rchar(roman.substr(d_, 1))
     end
   end d_
 return decnum

method rchar(arg) public static returns int

 parse arg.upper ch +1 .
 select case ch
   when 'M' then digit = 1000
   when 'D' then digit =  500
   when 'C' then digit =  100
   when 'L' then digit =   50
   when 'X' then digit =   10
   when 'V' then digit =    5
   when 'I' then digit =    1
   otherwise     digit =    0
   end
 return digit</lang>
Output:
     MCMXC: 1990
    MMVIII: 2008
   MDCLXVI: 1666

OCaml

<lang ocaml>let decimal_of_roman roman =

 let arabic = ref 0 in
 let lastval = ref 0 in
 for i = (String.length roman) - 1 downto 0 do
   let n =
     match roman.[i] with
     | 'M' | 'm' -> 1000
     | 'D' | 'd' -> 500
     | 'C' | 'c' -> 100
     | 'L' | 'l' -> 50
     | 'X' | 'x' -> 10
     | 'V' | 'v' -> 5
     | 'I' | 'i' -> 1
     | _ -> 0
   in
   if n < !lastval
   then arabic := !arabic - n
   else arabic := !arabic + n;
   lastval := n
 done;
 !arabic

let () =

 Printf.printf " %d\n" (decimal_of_roman "MCMXC");
 Printf.printf " %d\n" (decimal_of_roman "MMVIII");
 Printf.printf " %d\n" (decimal_of_roman "MDCLXVI");
</lang>

Mathematica

<lang Mathematica>FromDigits["MMCDV", "Roman"]</lang> returns 2405

PARI/GP

<lang parigp>fromRoman(s)={

 my(v=Vecsmall(s),key=vector(88),cur,t=0,tmp);
 key[73]=1;key[86]=5;key[88]=10;key[76]=50;key[67]=100;key[68]=500;key[77]=1000;
 cur=key[v[1]];
 for(i=2,#v,
   tmp=key[v[i]];
   if(!cur, cur=tmp; next);
   if(tmp>cur,
     t+=tmp-cur;
     cur=0
   ,
     t+=cur;
     cur=tmp
   )
 );
 t+cur

};</lang>

Perl

<lang Perl>use 5.10.0;

{

my @trans = (
               [M  => 1000],     [CM => 900], 
               [D  => 500],      [CD => 400], 
               [C  => 100],      [XC => 90],
               [L  => 50],       [XL => 40],
               [X  => 10],       [IX => 9],
               [V  => 5],        [IV => 4],
               [I  => 1],
);
 sub from_roman {
       my $r = shift;
       my $n = 0;
       foreach my $pair (@trans) {
         my ($k, $v) = @$pair;
         $n += $v while $r =~ s/^$k//i;
       }
       return $n
 }

}

say "$_: ", from_roman($_) for qw(MCMXC MDCLXVI MMVIII);</lang>

Output:
MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008

Perl 6

A non-validating version: <lang perl6>sub rom-to-num($r) {

   [+] gather $r.uc ~~ /
       ^
       [
       | M  { take 1000 }
       | CM { take 900 }
       | D  { take 500 }
       | CD { take 400 }
       | C  { take 100 }
       | XC { take 90 }
       | L  { take 50 }
       | XL { take 40 }
       | X  { take 10 }
       | IX { take 9 }
       | V  { take 5 }
       | IV { take 4 }
       | I  { take 1 }
       ]+
       $
   /;

}

say "$_ => &rom-to-num($_)" for <MCMXC MDCLXVI MMVIII>;</lang>

Output:
MCMXC => 1990
MDCLXVI => 1666
MMVIII => 2008

A validating version. Also handles older forms such as 'IIXX' and "IIII". <lang perl6>sub rom-to-num($r) {

   [+] gather $r.uc ~~ /
       ^
       ( (C*)M { take 1000 - 100 * $0.chars } )*
       ( (C*)D { take  500 - 100 * $0.chars } )?
       ( (X*)C { take  100 -  10 * $0.chars } )*
       ( (X*)L { take   50 -  10 * $0.chars } )?
       ( (I*)X { take   10 -       $0.chars } )*
       ( (I*)V { take    5 -       $0.chars } )?
       (     I { take    1                  } )*
       [ $ || { return NaN } ]
   /;

}

say "$_ => ", rom-to-num($_) for <MCMXC mdclxvi MMViii IIXX ILL>;</lang>

Output:
MCMXC => 1990
mdclxvi => 1666
MMViii => 2008
IIXX => 18
ILL => NaN

PicoLisp

<lang PicoLisp>(de roman2decimal (Rom)

  (let L (replace (chop Rom) 'M 1000 'D 500 'C 100 'L 50 'X 10 'V 5 'I 1)
     (sum '((A B) (if (>= A B) A (- A))) L (cdr L)) ) )</lang>

Test:

: (roman2decimal "MCMXC")
-> 1990

: (roman2decimal "MMVIII")
-> 2008

: (roman2decimal "MDCLXVI")
-> 1666

PHP

<lang PHP><?php /**

* @author Elad Yosifon <elad.yosifon@gmail.com>
*/

$roman_to_decimal = array( 'I' => 1, 'V' => 5, 'X' => 10, 'L' => 50, 'C' => 100, 'D' => 500, 'M' => 1000, );

/**

* @param $number
* @return int
*/

function roman2decimal($number) { global $roman_to_decimal;

// breaks the string into an array of chars $digits = str_split($number); $lastIndex = count($digits)-1; $sum = 0;

foreach($digits as $index => $digit) { if(!isset($digits[$index])) { continue; }

if(isset($roman_to_decimal[$digit])) { if($index < $lastIndex) { $left = $roman_to_decimal[$digits[$index]]; $right = $roman_to_decimal[$digits[$index+1]]; if($left < $right) { $sum += ($right - $left); unset($digits[$index+1],$left, $right); continue; } unset($left, $right); } } $sum += $roman_to_decimal[$digit]; }

return $sum; }

/*============= OUTPUT =============*/ header('Content-Type: text/plain');

$tests = array( "I" => array(roman2decimal('I'), 1), "II" => array(roman2decimal('II'), 2), "III" => array(roman2decimal('III'), 3), "IV" => array(roman2decimal('IV'), 4), "V" => array(roman2decimal('V'), 5), "VI" => array(roman2decimal('VI'), 6), "VII" => array(roman2decimal('VII'), 7), "IX" => array(roman2decimal('IX'), 9), "X" => array(roman2decimal('X'), 10), "XI" => array(roman2decimal('XI'), 11), "XIV" => array(roman2decimal('XIV'), 14), "XV" => array(roman2decimal('XV'), 15), "XVI" => array(roman2decimal('XVI'), 16), "XVIV" => array(roman2decimal('XVIV'), 19), "XIX" => array(roman2decimal('XIX'), 19), "MDCLXVI" => array(roman2decimal('MDCLXVI'), 1666), "MCMXC" => array(roman2decimal('MCMXC'), 1990), "MMVIII" => array(roman2decimal('MMVIII'), 2008), "MMMCLIX" => array(roman2decimal('MMMCLIX'), 3159), "MCMLXXVII" => array(roman2decimal('MCMLXXVII'), 1977), );


foreach($tests as $key => $value) { echo "($key == {$value[0]}) => " . ($value[0] === $value[1] ? "true" : "false, should be {$value[1]}.") . "\n"; }</lang>

Output:
(I == 1) => true
(II == 2) => true
(III == 3) => true
(IV == 4) => true
(V == 5) => true
(VI == 6) => true
(VII == 7) => true
(IX == 9) => true
(X == 10) => true
(XI == 11) => true
(XIV == 14) => true
(XV == 15) => true
(XVI == 16) => true
(XVIV == 19) => true
(XIX == 19) => true
(MDCLXVI == 1666) => true
(MCMXC == 1990) => true
(MMVIII == 2008) => true
(MMMCLIX == 3159) => true
(MCMLXXVII == 1977) => true

PL/I

<lang PL/I> test_decode: procedure options (main); /* 28 January 2013 */

  declare roman character (20) varying;
  do roman = 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'iix',
             'ix', 'x', 'xi', 'xiv', 'MCMLXIV', 'MCMXC', 'MDCLXVI',
             'MIM', 'MM', 'MMXIII';
     put skip list (roman, decode(roman));
  end;

decode: procedure (roman) returns (fixed(15));

  declare roman character (*) varying;
  declare (current, previous) character (1);
  declare n fixed (15);
  declare i fixed binary;
  previous = ; n = 0;
  do i = length(roman) to 1 by -1;
     current = substr(roman, i, 1);
     if digit_value(current) < digit_value(previous) then
        n = n - digit_value(current);
     else if digit_value(current) > digit_value(previous) then
        do;
           n = n + digit_value(current);
           previous = current;
        end;
     else
        n = n + digit_value(current);
  end;
  return (n);

end decode;

digit_value: procedure (roman_char) returns (fixed);

  declare roman_char character(1);
     select (roman_char);
        when ('M', 'm') return (1000);
        when ('D', 'd') return (500);
        when ('C', 'c') return (100);
        when ('L', 'l') return (50);
        when ('X', 'x') return (10);
        when ('V', 'v') return (5);
        when ('I', 'i') return (1);
        otherwise       return (0);
     end;

end digit_value;

end test_decode; </lang>

i                                        1 
ii                                       2 
iii                                      3 
iv                                       4 
v                                        5 
vi                                       6 
vii                                      7 
viii                                     8 
iix                                      8 
ix                                       9 
x                                       10 
xi                                      11 
xiv                                     14 
MCMLXIV                               1964 
MCMXC                                 1990 
MDCLXVI                               1666 
MIM                                   1999 
MM                                    2000 
MMXIII                                2013 

PL/SQL

<lang PL/SQL> /*****************************************************************

* $Author: Atanas Kebedjiev $
*****************************************************************
* PL/SQL code can be run as anonymous block.
* To test, execute the whole script or create the functions and then e.g. 'select rdecode('2012') from dual;
* Please note that task definition does not describe fully some current rules, such as
* * subtraction - IX XC CM are the valid subtraction combinations
* * A subtraction character cannot be repeated: 8 is expressed as VIII and not as IIX
* * V L and D cannot be used for subtraction
* * Any numeral cannot be repeated more than 3 times: 1910 should be MCMX and not MDCCCCX
* Code below does not validate the Roman numeral itself and will return a result even for a non-compliant number
* E.g. both MCMXCIX and IMM will return 1999 but the first one is the correct notation
*/

DECLARE

FUNCTION rvalue(c IN CHAR) RETURN NUMBER IS

   i INTEGER;

BEGIN

   i := 0;
   CASE (c)
       when 'M' THEN i := 1000;
       when 'D' THEN i := 500;
       when 'C' THEN i := 100;
       when 'L' THEN i := 50;
       when 'X' THEN i := 10;
       when 'V' THEN i := 5;
       when 'I' THEN i := 1;
   END CASE;
   RETURN i;

END;


FUNCTION decode(rn IN VARCHAR2) RETURN NUMBER IS

  i  INTEGER;
  l  INTEGER;
  cr CHAR;   -- current Roman numeral as substring from r
  cv INTEGER; -- value of current Roman numeral
  gr CHAR;   -- next Roman numeral
  gv NUMBER; --  value of the next numeral;
  dv NUMBER; -- decimal value to return

BEGIN

          l := length(rn);
          i := 1;
          dv := 0;
          while (i <= l)
          LOOP
               cr := substr(rn,i,1);
               cv := rvalue(cr);
  /* Look for a larger numeral in next position, like IV or CM  
     The number to subtract should be at least 1/10th of the bigger number
     CM and XC are valid, but IC and XM are not */
               IF (i < l) THEN
                  gr := substr(rn,i+1,1);
                  gv := rvalue(gr);
                  IF (cv < gv ) THEN
                     dv := dv - cv;
                  ELSE
                     dv := dv + cv;
                  END IF;
               ELSE
                  dv := dv + cv;
               END IF;  -- need to add the last value unconditionally
               i := i + 1;
           END LOOP;

RETURN dv;

END;

BEGIN

   DBMS_OUTPUT.PUT_LINE ('MMXII      = ' || rdecode('MMXII'));       -- 2012
   DBMS_OUTPUT.PUT_LINE ('MCMLI      = ' || rdecode('MCMLI'));       -- 1951
   DBMS_OUTPUT.PUT_LINE ('MCMLXXXVII = ' || rdecode('MCMLXXXVII'));  -- 1987
   DBMS_OUTPUT.PUT_LINE ('MDCLXVI    = ' || rdecode('MDCLXVI'));     -- 1666
   DBMS_OUTPUT.PUT_LINE ('MCMXCIX    = ' || rdecode('MCMXCIX'));     -- 1999

END; </lang>

Prolog

<lang Prolog>decode_digit(i, 1). decode_digit(v, 5). decode_digit(x, 10). decode_digit(l, 50). decode_digit(c, 100). decode_digit(d, 500). decode_digit(m, 1000).

decode_string(Sum, _, [], Sum).

decode_string(LastSum, LastValue, [Digit|Rest], NextSum) :-

  decode_digit(Digit, Value),
  Value < LastValue,
  Sum is LastSum - Value,
  decode_string(Sum, Value, Rest, NextSum).

decode_string(LastSum, LastValue, [Digit|Rest], NextSum) :-

  decode_digit(Digit, Value),
  Value >= LastValue,
  Sum is LastSum + Value,
  decode_string(Sum, Value, Rest, NextSum).

decode_string(Atom, Value) :-

  atom_chars(Atom, String),
  reverse(String, [Last|Rest]),
  decode_digit(Last, Start),
  decode_string(Start, Start, Rest, Value).

test :-

  decode_string(mcmxc, 1990),
  decode_string(mmviii, 2008),
  decode_string(mdclxvi, 1666).</lang>

The program above contains its own test predicate. The respective goal succeeds. Therefore the test passes.

PureBasic

<lang PureBasic>Procedure romanDec(roman.s)

 Protected i, n, lastval, arabic
   
 For i = Len(roman) To 1 Step -1
   Select UCase(Mid(roman, i, 1))
     Case "M"
       n = 1000
     Case "D"
       n = 500
     Case "C"
       n = 100
     Case "L"
       n = 50
     Case "X"
       n = 10
     Case "V"
       n = 5
     Case "I"
       n = 1
     Default
       n = 0
   EndSelect
   If (n < lastval)
     arabic - n
   Else
     arabic + n
   EndIf
   lastval = n
 Next 
 
 ProcedureReturn arabic

EndProcedure

If OpenConsole()

 PrintN(Str(romanDec("MCMXCIX"))) ;1999
 PrintN(Str(romanDec("MDCLXVI"))) ;1666
 PrintN(Str(romanDec("XXV")))     ;25
 PrintN(Str(romanDec("CMLIV")))   ;954
 PrintN(Str(romanDec("MMXI")))    ;2011
 
 Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
 CloseConsole()

EndIf</lang>

Output:
1999
1666
25
954
2011

Python

<lang python>_rdecode = dict(zip('MDCLXVI', (1000, 500, 100, 50, 10, 5, 1)))

def decode( roman ):

   result = 0
   for r, r1 in zip(roman, roman[1:]):
       rd, rd1 = _rdecode[r], _rdecode[r1]
       result += -rd if rd < rd1 else rd
   return result + _rdecode[roman[-1]]

if __name__ == '__main__':

   for r in 'MCMXC MMVIII MDCLXVI'.split():
       print( r, decode(r) )</lang>
Output:
MCMXC 1990
MMVIII 2008
MDCLXVI 1666

Another version, which I believe has clearer logic: <lang python>roman_values = (('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))

def roman_value(roman):

   total=0
   for symbol,value in reversed(roman_values):
       while roman.startswith(symbol):
           total += value
           roman = roman[len(symbol):]
   return total

if __name__=='__main__':

   for value in "MCMXC", "MMVIII", "MDCLXVI":
       print('%s = %i' % (value, roman_value(value)))

""" Output: MCMXC = 1990 MMVIII = 2008 MDCLXVI = 1666 """</lang>

Racket

<lang Racket>#lang racket (define (decode/roman number)

 (define letter-values
   (map cons '(#\M #\D #\C #\L #\X #\V #\I) '(1000 500 100 50 10 5 1)))
 (define (get-value letter)
   (cdr (assq letter letter-values)))
 (define lst (map get-value (string->list number)))
 (+ (last lst)
    (for/fold ((sum 0))
      ((i (in-list lst)) (i+1 (in-list (cdr lst))))
      (+ sum
         (if (> i+1 i)
             (- i)
             i)))))

(map decode/roman '("MCMXC" "MMVIII" "MDCLXVI"))

-> '(1990 2008 1666)</lang>

REXX

version 1

Translation of: NetRexx
Works with: Regina
Works with: ooRexx

<lang REXX>/* Rexx */

Do

       /* 1990  2008   1666    */
 years = 'MCMXC MMVIII MDCLXVI'
 Do y_ = 1 to words(years)
   Say right(word(years, y_), 10) || ':' decode(word(years, y_))
   End y_
 Return

End Exit

decode:

 Procedure

Do

 Parse upper arg roman .
 
 If verify(roman, 'MDCLXVI') = 0 then Do
   /* always insert the value of the least significant numeral */
   decnum = rchar(substr(roman, length(roman), 1))
   Do d_ = 1 to length(roman) - 1
     If rchar(substr(roman, d_, 1)) < rchar(substr(roman, d_ + 1, 1)) then Do
       /* Handle cases where numerals are not in descending order */
       /*   subtract the value of the numeral */
       decnum = decnum - rchar(substr(roman, d_, 1))
       End
     else Do
       /* Normal case */
       /*   add the value of the numeral */
       decnum = decnum + rchar(substr(roman, d_, 1))
       End
     End d_
   End
 else Do
   decnum = roman 'contains invalid roman numerals'
   End
 Return decnum

End Exit

rchar:

 Procedure

Do

 Parse upper arg ch +1 .
 select 
   when ch = 'M' then digit = 1000
   when ch = 'D' then digit =  500
   when ch = 'C' then digit =  100
   when ch = 'L' then digit =   50
   when ch = 'X' then digit =   10
   when ch = 'V' then digit =    5
   when ch = 'I' then digit =    1
   otherwise          digit =    0
   end
 Return digit

End Exit</lang>

Output:
     MCMXC: 1990
    MMVIII: 2008
   MDCLXVI: 1666

version 2

This version of the (above) REXX program:

  • removes 3 sets of superfluous DO-END statements
  • removes dead code (3 REXX statements that can't be executed)
  • replaced SUBSTR(xxx, length(xxx), 1)   with   RIGHT(xxx,1)
  • removes a useless PARSE statement
  • compresses 63 lines to 29 lines
  • reordered IF statements by most likely to occur

<lang rexx>/*REXX program to convert Roman numeral number(s) to Arabic number(s). */ rYear = 'MCMXC'  ; say right(rYear,9)':' rom2dec(rYear) rYear = 'mmviii'  ; say right(rYear,9)':' rom2dec(rYear) rYear = 'MDCLXVI'  ; say right(rYear,9)':' rom2dec(rYear) exit

rom2dec: procedure; arg roman . if verify(roman,'MDCLXVI')\==0 then do

                                    say  'invalid Roman number:'  roman
                                    return '***error!***'
                                    end
  1. =rChar(right(roman,1)) /*start with the last numeral*/
           do j=1  for length(roman) - 1
           x=rChar(substr(roman,j  ,1))   /*the current Roman numeral. */
           y=rChar(substr(roman,j+1,1))   /*the    next Roman numeral. */
           if x<y  then # = #-x           /* x<y ?    Then subtract it.*/
                   else # = #+x           /* x≥y ?    Then add it.     */
           end   /*j*/

return #

rChar: procedure; arg _ /*convert a Roman number to an Arabic dig*/ if _=='I' then return 1 if _=='V' then return 5 if _=='X' then return 10 if _=='L' then return 50 if _=='C' then return 100 if _=='D' then return 500 if _=='M' then return 1000

               return    0    /*_ is an invalid Roman numeral (char).  */</lang>

version 3

This REXX version allows the use of   j   which was being used in the later part of the Holy Roman Empire (as a trailing   i   in Roman numeral numbers).
Also, this program converts IIXX correctly. Note: this number was actually chiseled in Roman monuments, archways, and tombs/crypts.
Also supported are larger numbers such as (M) which is a Roman numeral(s) within a grouping symbol, in this case, a set of parenthesis.
Deep parentheses are also supported: (MM) is two million, ((MMM)) is three billion.

Normally, the Romans used an overbar (vinculum) for larger numbers (such as XX for twenty-thousand),
but the use of such a character is very problematic for computers to deal with, so parenthesis are used instead.
The Romans also had symbols for some fractions which would be a good addition to this task.
Also, lowercase u was also used for lowercase v
Also note that IIII is a legal Roman numeral construct; just check almost any old clock or "dialed" wristwatch that has Roman numerals. <lang rexx>/*REXX program to convert Roman numerals ──► Arabic numerals (numbers).*/ numeric digits 1000 /*we can handle big nums.*/ y = 'MCMXC'  ; say right(y,9)':' rom2dec(y) y = 'mmviii'  ; say right(y,9)':' rom2dec(y) y = 'MDCLXVI'  ; say right(y,9)':' rom2dec(y) y = 'MDQLXVI'  ; say right(y,9)':' rom2dec(y) exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────ROM2DEC subroutine──────────────────*/ rom2dec: procedure; h='0'x; #=0; $=1; arg n . /*uppercase N.*/ n=translate(n,'()()',"[]{}"); _ = verify(n,'MDCLXVUIJ()') if _\==0 then return '***error!*** invalid Roman numeral:' substr(n,_,1) @.=1; @.m=1000; @.d=500; @.c=100; @.l=50; @.x=10; @.u=5; @.v=5

  do k=length(n) to 1 by -1; _=substr(n,k,1)  /*examine a Roman numeral*/
                                              /*(next) scale up or down*/
  if _=='(' | _==')' then  do; $=$*1000; if _=='(' then $=1; iterate; end
  _=@._*$                                     /*scale it if necessary. */
  if _>h  then h=_                            /*remember Roman numeral.*/
  if _<h  then #=#-_                          /*char>next?  Then sub.  */
          else #=#+_                          /*            else add.  */
  end   /*k*/

return # /*return Arabic number. */</lang> output (input is within the REXX program):

    MCMXC: 1990
   mmviii: 2008
  MDCLXVI: 1666
  MDQLXVI: ***error!*** invalid Roman numeral: Q

Ruby

<lang ruby>def fromRoman(roman)

 r = roman.dup.upcase
 n = 0
 until r.empty? do
   case
   when r.start_with?('M')  then v = 1000; len = 1
   when r.start_with?('CM') then v = 900;  len = 2
   when r.start_with?('D')  then v = 500;  len = 1
   when r.start_with?('CD') then v = 400;  len = 2
   when r.start_with?('C')  then v = 100;  len = 1
   when r.start_with?('XC') then v = 90;   len = 2
   when r.start_with?('L')  then v = 50;   len = 1
   when r.start_with?('XL') then v = 40;   len = 2
   when r.start_with?('X')  then v = 10;   len = 1
   when r.start_with?('IX') then v = 9;    len = 2
   when r.start_with?('V')  then v = 5;    len = 1
   when r.start_with?('IV') then v = 4;    len = 2
   when r.start_with?('I')  then v = 1;    len = 1
   else
     raise ArgumentError.new("invalid roman numerals: " + roman)
   end
   n += v
   r[0 .. len-1] = ""
 end
 n

end

[ "MCMXC", "MMVIII", "MDCLXVI" ].each {|r| pp [r, fromRoman(r)]}</lang>

Output:
["MCMXC", 1990]
["MMVIII", 2008]
["MDCLXVI", 1666]

Run BASIC

<lang runbasic>print "MCMXCIX = "; romToDec( "MCMXCIX") '1999 print "MDCLXVI = "; romToDec( "MDCLXVI") '1666 print "XXV = "; romToDec( "XXV") '25 print "CMLIV = "; romToDec( "CMLIV") '954 print "MMXI = "; romToDec( "MMXI") '2011

function romToDec(roman$)

 for i  = len(roman$) to 1 step -1
  x$    = mid$(roman$, i, 1)
  n     = 0
  if x$ = "M" then n = 1000
  if x$ = "D" then n = 500
  if x$ = "C" then n = 100
  if x$ = "L" then n = 50
  if x$ = "X" then n = 10
  if x$ = "V" then n = 5
  if x$ = "I" then n = 1

 if n < preNum then num = num - n else num = num + n
 preNum = n
 next

 romToDec =num

end function</lang>

Scala

<lang Scala>def fromRoman( r:String ) : Int = {

 val arabicNumerals = List("CM"->900,"M"->1000,"CD"->400,"D"->500,"XC"->90,"C"->100,
                           "XL"->40,"L"->50,"IX"->9,"X"->10,"IV"->4,"V"->5,"I"->1)	
                           
 var s = r                          
 arabicNumerals.foldLeft(0){ (n,t) => { 
   val l = s.length; s = s.replaceAll(t._1,""); val c = (l - s.length)/t._1.length  // Get the frequency
   n + (c*t._2)  // Add the arabic numerals up  
 } }

}


// A small test def test( roman:String ) = println( roman + " => " + fromRoman( roman ) )

test("MCMXC") test("MMVIII") test("MDCLXVI")</lang>

Output:
MCMXC => 1990
MMVIII => 2008
MDCLXVI => 1666

Seed7

<lang seed7>$ include "seed7_05.s7i";

const func integer: ROMAN parse (in string: roman) is func

 result
   var integer: arabic is 0;
 local
   var integer: index is 0;
   var integer: number is 0;
   var integer: lastval is 0;
 begin
   for index range length(roman) downto 1 do
     case roman[index] of
       when {'M', 'm'}: number := 1000;
       when {'D', 'd'}: number :=  500;
       when {'C', 'c'}: number :=  100;
       when {'L', 'l'}: number :=   50;
       when {'X', 'x'}: number :=   10;
       when {'V', 'v'}: number :=    5;
       when {'I', 'i'}: number :=    1;
       otherwise:       raise RANGE_ERROR;
     end case;
     if number < lastval then
       arabic -:= number;
     else
       arabic +:= number;
     end if;
     lastval := number;
   end for;
 end func;

const proc: main is func

 begin
   writeln(ROMAN parse "MCMXC");
   writeln(ROMAN parse "MMVIII");
   writeln(ROMAN parse "MDCLXVI");
 end func;</lang>

Original source: [1]

Output:
1990
2008
1666

SNOBOL4

<lang SNOBOL4>* Roman to Arabic

       define('arabic(n)s,ch,val,sum,x') :(arabic_end)

arabic s = 'M1000 D500 C100 L50 X10 V5 I1 '

       n = reverse(n)

arab1 n len(1) . ch = :f(arab2)

       s ch break(' ') . val
       val = lt(val,x) (-1 * val)
       sum = sum + val; x = val        :(arab1)

arab2 arabic = sum  :(return) arabic_end

  • Test and display
       tstr = 'MMX MCMXCIX MCDXCII MLXVI CDLXXVI "

tloop tstr break(' ') . r span(' ') = :f(out)

       astr = astr r '=' arabic(r) ' ' :(tloop)

out output = astr end</lang>

Output:
MMX=2010 MCMXCIX=1999 MCDXCII=1492 MLXVI=1066 CDLXXVI=476

Here's an alternative version, which is maybe more SNOBOL4-idiomatic and less like one might program it in a more common language: <lang SNOBOL4>* Roman to Arabic define("arabic1(romans,arabic1)rdigit,adigit,b4") romans1 = " 0 IX9 IV4 III3 II2 I1 VIII8 VII7 VI6 V5"  :(arabic1_end) arabic1 ident(romans) :s(return) romans (break("IV") | rem) . b4 rem . rdigit = b4

       romans1 " " rdigit any("0123456789") . adigit 

arabic1 = adigit arabic1

       romans = replace(romans,"MDCLX","CLXVI")  :(arabic1)

arabic1_end

  • Test and display
       tstr = "MMX MCMXCIX MCDXCII MLXVI CDLXXVI "

tloop tstr break(' ') . r span(' ') = :f(out)

       astr = astr r '=' arabic1(r) ' '          :(tloop)

out output = astr end</lang> The output is the same as in the earlier version.

The following version takes advantage of some of the so-called "SPITBOL extensions", which are to be found in most modern implementations. This allows removing several labels and explicit transfers of control, and moves some of the looping into the pattern matcher. Again, the output is the same. <lang SNOBOL4>* Roman to Arabic define("arabic1(romans,arabic1)rdigit,adigit,b4") romans1 = " 0 IX9 IV4 III3 II2 I1 VIII8 VII7 VI6 V5"  :(arabic1_end) arabic1 ident(romans) :s(return) romans (break("IV") | rem) . b4 rem . rdigit = replace(b4,"MDCLX","CLXVI")

       romans1 " " rdigit any("0123456789") . adigit 

arabic1 = adigit arabic1  :(arabic1) arabic1_end

  • Test and display
       tstr = " MMX MCMXCIX MCDXCII MLXVI CDLXXVI "
       tstr span(' ') break(' ') $ r *?(astr = astr r '=' arabic1(r) ' ') fail
       output = astr

end</lang>

Tcl

As long as we assume that we have a valid roman number, this is most easily done by transforming the number into a sum and evaluating the expression: <lang tcl>proc fromRoman rnum {

   set map {M 1000+ CM 900+ D 500+ CD 400+ C 100+ XC 90+ L 50+ XL 40+ X 10+ IX 9+ V 5+ IV 4+ I 1+}
   expr [string map $map $rnum]0}

}</lang> Demonstrating: <lang tcl>foreach r {MCMXC MDCLXVI MMVIII} {

   puts "$r\t-> [fromRoman $r]"

}</lang>

Output:
MCMXC	-> 1990
MDCLXVI	-> 1666
MMVIII	-> 2008

TI-83 BASIC

Using the Rom‣Dec function "real(21," from Omnicalc. <lang ti83b>PROGRAM:ROM2DEC

Input Str1
Disp real(21,Str1)</lang>

Using TI-83 BASIC <lang ti83b>PROGRAM:ROM2DEC

Input "ROMAN:",Str1
{1000,500,100,50,10,5,1}➞L1
0➞P
0➞Y
For(I,length(Str1),1,-1)
 :inString("MDCLXVI",sub(Str1,I,1))➞X
 :If X≤0:Then
   :Disp "BAD NUMBER"
   :Stop
 :End
 :L1(x)➞N
 :If N<P:Then
   :Y–N➞Y
 :Else
   :Y+N➞Y
 :End
 :N➞P
End
Disp Y</lang>

TUSCRIPT

<lang tuscript>$$ MODE TUSCRIPT LOOP roman_number="MCMXC'MMVIII'MDCLXVI" arab_number=DECODE (roman_number,ROMAN) PRINT "Roman number ",roman_number," equals ", arab_number ENDLOOP</lang>

Output:
Roman number MCMXC equals 1990
Roman number MMVIII equals 2008
Roman number MDCLXVI equals 1666

Vedit macro language

<lang vedit>// Main program for testing the function // do {

   Get_Input(10, "Enter a roman numeral: ", NOCR+STATLINE)
   Call("Roman_to_Arabic")
   Reg_Type(10) Message("	= ") Num_Type(#1)

} while(#1) Return

// Convert Roman numeral into numeric value // in: @10 = Roman numeral // out: #1 = numeric value //

Roman_to_Arabic:
   Buf_Switch(Buf_Free)
   Ins_Text("M1000 D500 C100 L50 X10 V5 I1") Ins_Newline
   Reg_Ins(10) Ins_Char(' ')
   #1 = #2 = 0
   Repeat(ALL) {
       #3 = #2                                  // #3 = previous character
       Goto_Line(2)                             // roman numeral to be converted
       if (At_EOL) {
           Break                                // all done
       }
       Reg_Copy_Block(11, CP, CP+1, DELETE)     // next character in roman numeral
       if (Search(@11, BEGIN+ADVANCE+NOERR)) {  // find character from the table
           #2 = Num_Eval(SUPPRESS)              // corresponding numeric value
           if (#2 > #3) {                       // larger than previous digit?
               #1 -= #3                         // substract previous digit
           } else {
               #1 += #3                         // add previous digit
           }
       }
   }
   Reg_Empty(11)
   Buf_Quit(OK)

Return</lang>

Output:
iv	=     4
xii	=    12
MDCLXVI	=  1666
MCMXC	=  1990
MMXI	=  2011

XPL0

<lang XPL0>string 0; \use zero-terminated strings code CrLf=9, IntOut=11;

func Roman(Str); \Convert Roman numeral string to decimal value char Str; int I, Val, Val0, Sum; [I:= 0; Sum:= 0; Val0:= 5000; loop [case Str(I) of

         ^M: Val:= 1000;
         ^D: Val:= 500;
         ^C: Val:= 100;
         ^L: Val:= 50;
         ^X: Val:= 10;
         ^V: Val:= 5;
         ^I: Val:= 1
       other return Sum;       \zero string terminator
       I:= I+1;
       Sum:= Sum + Val;
       if Val > Val0 then Sum:= Sum - 2*Val0;
       Val0:= Val;
       ];

];

[IntOut(0, Roman("MCMXC")); CrLf(0);

IntOut(0, Roman("MMVIII"));   CrLf(0);
IntOut(0, Roman("MDCLXVI"));  CrLf(0);

]</lang>

Output:

1990
2008
1666

Zsh

<lang zsh>function parseroman () {

 local max=0 sum i j
 local -A conv
 conv=(I 1 V 5 X 10 L 50 C 100 D 500 M 1000)
 for j in ${(Oas::)1}; do
   i=conv[$j]
   if (( i >= max )); then
     (( sum+=i ))
     (( max=i ))
   else
     (( sum-=i ))
   fi
 done
 echo $sum

}</lang>