Roman numerals/Decode

From Rosetta Code
Jump to: navigation, search
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.

Contents

[edit] Ada

Pragma Ada_2012;
Pragma Assertion_Policy( Check );
 
With
Unchecked_Conversion,
Ada.Text_IO;
 
Procedure Test_Roman_Numerals is
 
-- We create an enumeration of valid characters, note that they are
-- character-literals, this is so that we can use literal-strings,
-- and that their size is that of Integer.
Type Roman_Digits is ('I', 'V', 'X', 'L', 'C', 'D', 'M' )
with Size => Integer'Size;
 
-- We use a representation-clause ensure the proper integral-value
-- of each individual character.
For Roman_Digits use
(
'I' => 1,
'V' => 5,
'X' => 10,
'L' => 50,
'C' => 100,
'D' => 500,
'M' => 1000
);
 
-- To convert a Roman_Digit to an integer, we now only need to
-- read its value as an integer.
Function Convert is new Unchecked_Conversion
( Source => Roman_Digits, Target => Integer );
 
-- Romena_Numeral is a string of Roman_Digit.
Type Roman_Numeral is array (Positive range <>) of Roman_Digits;
 
-- The Numeral_List type is used herein only for testing
-- and verification-data.
Type Numeral_List is array (Positive range <>) of
not null access Roman_Numeral;
 
-- The Test_Cases subtype ensures that Test_Data and Validation_Data
-- both contain the same number of elements, and that the indecies
-- are the same; essentially the same as:
--
-- pragma Assert( Test_Data'Length = Validation_Data'Length
-- AND Test_Data'First = Validation_Data'First);
 
subtype Test_Cases is Positive range 1..14;
 
Test_Data : constant Numeral_List(Test_Cases):=
(
New Roman_Numeral'("III"), -- 3
New Roman_Numeral'("XXX"), -- 30
New Roman_Numeral'("CCC"), -- 300
New Roman_Numeral'("MMM"), -- 3000
 
New Roman_Numeral'("VII"), -- 7
New Roman_Numeral'("LXVI"), -- 66
New Roman_Numeral'("CL"), -- 150
New Roman_Numeral'("MCC"), -- 1200
 
New Roman_Numeral'("IV"), -- 4
New Roman_Numeral'("IX"), -- 9
New Roman_Numeral'("XC"), -- 90
 
New Roman_Numeral'("ICM"), -- 901
New Roman_Numeral'("CIM"), -- 899
 
New Roman_Numeral'("MDCLXVI") -- 1666
);
 
Validation_Data : constant array(Test_Cases) of Natural:=
( 3, 30, 300, 3000,
7, 66, 150, 1200,
4, 9, 90,
901, 899,
1666
);
 
 
-- In Roman numerals, the subtractive form [IV = 4] was used
-- very infrequently, the most common form was the addidive
-- form [IV = 6]. (Consider military logistics and squads.)
 
-- SUM returns the Number, read in the additive form.
Function Sum( Number : Roman_Numeral ) return Natural is
begin
Return Result : Natural:= 0 do
For Item of Number loop
Result:= Result + Convert( Item );
end loop;
End Return;
end Sum;
 
-- EVAL returns Number read in the subtractive form.
Function Eval( Number : Roman_Numeral ) return Natural is
Current : Roman_Digits:= 'I';
begin
Return Result : Natural:= 0 do
For Item of Number loop
if Current < Item then
Result:= Convert(Item) - Result;
Current:= Item;
else
Result:= Result + Convert(Item);
end if;
end loop;
End Return;
end Eval;
 
-- Display the given Roman_Numeral via Text_IO.
Procedure Put( S: Roman_Numeral ) is
begin
For Ch of S loop
declare
-- The 'Image attribute returns the character inside
-- single-quotes; so we select the character itself.
C : Character renames Roman_Digits'Image(Ch)(2);
begin
Ada.Text_IO.Put( C );
end;
end loop;
end;
 
-- This displays pass/fail dependant on the parameter.
Function PF ( Value : Boolean ) Return String is
begin
Return Result : String(1..4):= ( if Value then"pass"else"fail" );
End PF;
 
Begin
Ada.Text_IO.Put_Line("Starting Test:");
 
for Index in Test_Data'Range loop
declare
Item  : Roman_Numeral renames Test_Data(Index).all;
Value : constant Natural := Eval(Item);
begin
Put( Item );
 
Ada.Text_IO.Put( ASCII.HT & "= ");
Ada.Text_IO.Put( Value'Img );
Ada.Text_IO.Put_Line( ASCII.HT & '[' &
PF( Value = Validation_Data(Index) )& ']');
end;
end loop;
 
 
Ada.Text_IO.Put_Line("Testing complete.");
End Test_Roman_Numerals;
 

Output:

Starting Test:
III	=  3	[pass]
XXX	=  30	[pass]
CCC	=  300	[pass]
MMM	=  3000	[pass]
VII	=  7	[pass]
LXVI	=  66	[pass]
CL	=  150	[pass]
MCC	=  1200	[pass]
IV	=  4	[pass]
IX	=  9	[pass]
XC	=  90	[pass]
ICM	=  901	[pass]
CIM	=  899	[pass]
MDCLXVI	=  1666	[pass]
Testing complete.

[edit] 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.

    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

[edit] ANTLR

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


[edit] 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' ;

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

[edit] AutoHotkey

Works with: AutoHotkey_L
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
Output:
MCMXC	= 1990
MMVIII	= 2008
MDCLXVI	= 1666

[edit] 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 : "" )
}
Output:
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666

[edit] BBC BASIC

      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%

Output:

MCMXCIX         1999
MMXII           2012
MDCLXVI         1666
MMMDCCCLXXXVIII           3888

[edit] Bracmat

Translation of: Icon and Unicon
  ( 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
);
Output:
M 1000
MCXI 1111
CMXI 911
MCM 1900
MCMXC 1990
MMVIII 2008
MMIX 2009
MCDXLIV 1444
MDCLXVI 1666
MMXII 2012

[edit] C

Note: the code deliberately did not distinguish between "I", "J" or "U", "V", doing what Romans did for fun.

#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 */

#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;
}

[edit] C++

 
#include <exception>
#include <string>
#include <iostream>
using namespace std;
 
namespace Roman
{
int ToInt(char c)
{
switch (c)
{
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
}
throw exception("Invalid character");
}
 
int ToInt(const string& s)
{
int retval = 0, pvs = 0;
for (auto pc = s.rbegin(); pc != s.rend(); ++pc)
{
const int inc = ToInt(*pc);
retval += inc < pvs ? -inc : inc;
pvs = inc;
}
return retval;
}
}
 
int main(int argc, char* argv[])
{
try
{
cout << "MCMXC = " << Roman::ToInt("MCMXC") << "\n";
cout << "MMVIII = " << Roman::ToInt("MMVIII") << "\n";
cout << "MDCLXVI = " << Roman::ToInt("MDCLXVI") << "\n";
}
catch (exception& e)
{
cerr << e.what();
return -1;
}
return 0;
}
 
Output:
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666

[edit] C#

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.
}
}
}
Output:
MCMXC: 1990
MMVIII: 2008
MDCLXVI: 1666

[edit] 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)))))
Output:
(map ro2ar ["MDCLXVI" "MMMCMXCIX" "XLVIII" "MMVIII"])
(1666 3999 48 2008)

[edit] COBOL

 
IDENTIFICATION DIVISION.
PROGRAM-ID. UNROMAN.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 filler.
03 i pic 9(02) comp.
03 j pic 9(02) comp.
03 k pic 9(02) comp.
03 l pic 9(02) comp.
01 inp-roman.
03 inp-rom-ch pic x(01) occurs 20 times.
01 inp-roman-digits.
03 inp-rom-digit pic 9(01) occurs 20 times.
01 ws-search-idx pic 9(02) comp.
01 ws-tbl-table-def.
03 filler pic x(05) value '1000M'.
03 filler pic x(05) value '0500D'.
03 filler pic x(05) value '0100C'.
03 filler pic x(05) value '0050L'.
03 filler pic x(05) value '0010X'.
03 filler pic x(05) value '0005V'.
03 filler pic x(05) value '0001I'.
01 filler redefines ws-tbl-table-def.
03 ws-tbl-roman occurs 07 times indexed by rx.
05 ws-tbl-rom-val pic 9(04).
05 ws-tbl-rom-ch pic x(01).
01 ws-number pic s9(05) value 0.
01 ws-number-pic pic zzzz9-.
 
PROCEDURE DIVISION.
accept inp-roman
perform
until inp-roman = ' '
move zeroes to inp-roman-digits
perform
varying i from 1 by +1 until inp-rom-ch (i) = ' '
set rx to 1
search ws-tbl-roman
at end
move 0 to inp-rom-digit (i)
when ws-tbl-rom-ch (rx) = inp-rom-ch (i)
set inp-rom-digit (i) to rx
end-search
end-perform
compute l = i - 1
move 0 to ws-number
perform
varying i from 1 by +1
until i > l or inp-rom-digit (i) = 0
compute j = inp-rom-digit (i)
compute k = inp-rom-digit (i + 1)
if ws-tbl-rom-val (k)
> ws-tbl-rom-val (j)
compute ws-number
= ws-number
- ws-tbl-rom-val (j)
else
compute ws-number
= ws-number
+ ws-tbl-rom-val (j)
end-if
end-perform
move ws-number to ws-number-pic
display '----------'
display 'roman=' inp-roman
display 'arabic=' ws-number-pic
if i < l or ws-number = 0
display 'invalid/incomplete roman numeral at pos 'i
' found ' inp-rom-ch (i)
end-if
accept inp-roman
end-perform
stop run
.
END PROGRAM UNROMAN.
 

Output
input was supplied via STDIN

----------
roman=MCMLXXXVIII
        
arabic= 1988 
----------
roman=MIX
                
arabic= 1009 
----------
roman=MDCCCLXXXVII
       
arabic= 1887 
----------
roman=IX
                 
arabic=    9 
----------
roman=MMMDCCCLXXXVIII
    
arabic= 3888 
----------
roman=K
                  
arabic=    0 
invalid/incomplete roman numeral at pos 01 found K
----------
roman=MIXT
               
arabic= 1009 
invalid/incomplete roman numeral at pos 04 found T
----------
roman=MCMB
               
arabic= 1900 
invalid/incomplete roman numeral at pos 04 found B

[edit] 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}"

[edit] Common Lisp

 
(defun mapcn (chars nums string)
(loop as char across string as i = (position char chars) collect (and i (nth i nums))))
 
(defun parse-roman (R)
(loop with nums = (mapcn "IVXLCDM" '(1 5 10 50 100 500 1000) R)
as (A B) on nums if A sum (if (and B (< A B)) (- A) A)))
 

Description:

Mapcn is a function to map characters to numbers.  It uses the mapping between its first two arguments, chars and nums,
to map its 3rd argument, string, to a list of numbers.  If a character of string is missing from chars, its number will be
nil.

Parse-roman uses mapcn to map R to a list of numbers, then iterates that list with A and B, adding A to the total whenever
it's not less than B, and subtracting it when it is.  If A is nil, it's skipped.  Such as when the character is not Roman.
If B is nil, A is added and not subtracted.  Such as at the end of the list, or when a non-Roman character, such as a space,
is embedded in the Roman.

Test code:

(dolist (r '("MCMXC" "MDCLXVI" "MMVIII"))
(format t "~a:~10t~d~%" r (parse-roman r)))
Output:
MCMXC:    1990
MDCLXVI:  1666
MMVIII:   2008

[edit] 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.matchAll("CM|CD|XC|XL|IX|IV|[MDCLXVI]".regex))
arabic += weights[symbols.countUntil(m.hit)];
return arabic;
}
 
void main() {
assert("MCMXC".toArabic == 1990);
assert("MMVIII".toArabic == 2008);
assert("MDCLXVI".toArabic == 1666);
}

Alternative more functional version:

import std.regex, std.algorithm;
 
immutable int[string] w2s;
 
pure 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*/ {
return s
.matchAll("CM|CD|XC|XL|IX|IV|[MDCLXVI]".regex)
.map!(m => w2s[m.hit])
.sum;
}
 
void main() {
assert("MCMXC".toArabic == 1990);
assert("MMVIII".toArabic == 2008);
assert("MDCLXVI".toArabic == 1666);
}

[edit] Delphi/Pascal

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.

[edit] ECL

The best declarative approach:

 
MapChar(STRING1 c) := CASE(c,'M'=>1000,'D'=>500,'C'=>100,'L'=>50,'X'=>10,'V'=>5,'I'=>1,0);
 
RomanDecode(STRING s) := FUNCTION
dsS := DATASET([{s}],{STRING Inp});
R := { INTEGER2 i; };
 
R Trans1(dsS le,INTEGER pos) := TRANSFORM
SELF.i := MapChar(le.Inp[pos]) * IF ( MapChar(le.Inp[pos]) < MapChar(le.Inp[pos+1]), -1, 1 );
END;
 
RETURN SUM(NORMALIZE(dsS,LENGTH(TRIM(s)),Trans1(LEFT,COUNTER)),i);
END;
 
RomanDecode('MCMLIV'); //1954
RomanDecode('MCMXC'); //1990
RomanDecode('MMVIII'); //2008
RomanDecode('MDCLXVI'); //1666
RomanDecode('MDLXVI'); //1566

Here's an alternative that emulates the wat procedural code would approach the problem:

IMPORT STD;
RomanDecode(STRING s) := FUNCTION
SetWeights := [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
SetSymbols := ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
ProcessRec := RECORD
UNSIGNED val;
STRING Roman;
END;
dsSymbols := DATASET(13,TRANSFORM(ProcessRec,SELF.Roman := s, SELF := []));
 
RECORDOF(dsSymbols) XF(dsSymbols L, dsSymbols R, INTEGER C) := TRANSFORM
ThisRoman := IF(C=1,R.Roman,L.Roman);
IsDone := ThisRoman = '';
Repeatable := C IN [1,5,9,13];
SymSize := IF(C % 2 = 0, 2, 1);
IsNext := STD.Str.StartsWith(ThisRoman,SetSymbols[C]);
SymLen := IF(IsNext,
IF(NOT Repeatable,
SymSize,
MAP(NOT IsDone AND ThisRoman[1] = ThisRoman[2] AND ThisRoman[1] = ThisRoman[3] => 3,
NOT IsDone AND ThisRoman[1] = ThisRoman[2] => 2,
NOT IsDone => 1,
0)),
0);
 
SymbolWeight(STRING s) := IF(NOT Repeatable,
SetWeights[C],
CHOOSE(LENGTH(s),SetWeights[C],SetWeights[C]*2,SetWeights[C]*3,0));
 
SELF.Roman := IF(IsDone,ThisRoman,ThisRoman[SymLen+1..]);
SELF.val  := IF(IsDone,L.val,L.Val + IF(IsNext,SymbolWeight(ThisRoman[1..SymLen]),0));
END;
i := ITERATE(dsSymbols,XF(LEFT,RIGHT,COUNTER));
RETURN i[13].val;
END;
 
RomanDecode('MCMLIV'); //1954
RomanDecode('MCMXC'); //1990
RomanDecode('MMVIII'); //2008
RomanDecode('MDCLXVI'); //1666
RomanDecode('MDLXVI'); //1566

[edit] Emacs 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
 
Output:
(ro2ar '(M D C L X V I)) -> 1666

[edit] Erlang

Putting the character X into a list, [X], creates a string with a single character.

 
-module( roman_numerals ).
 
-export( [decode_from_string/1]).
 
to_value($M) -> 1000;
to_value($D) -> 500;
to_value($C) -> 100;
to_value($L) -> 50;
to_value($X) -> 10;
to_value($V) -> 5;
to_value($I) -> 1.
 
decode_from_string([]) -> 0;
decode_from_string([H1]) -> to_value(H1);
decode_from_string([H1, H2 |Rest]) ->
case {to_value(H1), to_value(H2)} of
{V1, V2} when V1 < V2 -> V2 - V1 + decode_from_string(Rest);
{V1, V1} -> V1 + V1 + decode_from_string(Rest);
{V1, _} -> V1 + decode_from_string([H2|Rest])
end.
 
Output:
10> roman_numerals:decode_from_string("MCMXC").
1990
11> roman_numerals:decode_from_string("MMVIII").
2008
12> roman_numerals:decode_from_string("MDCLXVI").
1666

[edit] Euphoria

Translation of: PureBasic
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")
Output:
1999
1666
25
954
2011

[edit] 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.

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)
;;

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.

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
;;

Test code:

let tests = ["MCMXC"; "MMVIII"; "MDCLXVII"; "MMMCLIX"; "MCMLXXVII"; "MMX"]
for test in tests do Printf.printf "%s: %d\n" test (decimal_of_roman test)
;;
Output:
MCMXC: 1990
MMVIII: 2008
MDCLXVII: 1667
MMMCLIX: 3159
MCMLXXVII: 1977
MMX: 2010

[edit] 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 .

[edit] Fortran

Works with: Fortran version 90 and later
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
Output:
        1990        2008        1666

[edit] Go

For fluff, the unicode overbar is recognized as a factor of 1000, as described in WP.

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)
}
}
}
Output:
MCMXC == 1990
MMVIII == 2008
MDCLXVI == 1666

Simpler:

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))
}
}

[edit] Groovy

Solution:

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
}
}
}

Test:

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')}
"""
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

[edit] 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 ]

Usage:

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

[edit] Icon and Unicon

link numbers
 
procedure main()
every R := "MCMXC"|"MDCLXVI"|"MMVIII" do
write(R, " = ",unroman(R))
end

numbers.icn provides unroman

The code for this procedure is copied below:

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
Output:
MCMXC = 1990
MDCLXVI = 1666
MMVIII = 2008

[edit] J

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

Example use:

   rom2d 'MCMXC'
1990
rom2d 'MDCLXVI'
1666
rom2d 'MMVIII'
2008

[edit] Java

Works with: Java version 1.5+
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
}
}
Output:
1990
2008
1666
Works with: Java version 1.8+
import java.util.Set;
import java.util.EnumSet;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
 
public interface RomanNumerals {
public enum Numeral {
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);
 
public final long weight;
 
private static final Set<Numeral> SET = Collections.unmodifiableSet(EnumSet.allOf(Numeral.class));
 
private Numeral(long weight) {
this.weight = weight;
}
 
public static Numeral getLargest(long weight) {
return SET.stream()
.filter(numeral -> weight >= numeral.weight)
.findFirst()
.orElse(I)
;
}
};
 
public static String encode(long n) {
return LongStream.iterate(n, l -> l - Numeral.getLargest(l).weight)
.limit(Numeral.values().length)
.filter(l -> l > 0)
.mapToObj(Numeral::getLargest)
.map(String::valueOf)
.collect(Collectors.joining())
;
}
 
public static long decode(String roman) {
long result = new StringBuilder(roman.toUpperCase()).reverse().chars()
.mapToObj(c -> Character.toString((char) c))
.map(numeral -> Enum.valueOf(Numeral.class, numeral))
.mapToLong(numeral -> numeral.weight)
.reduce(0, (a, b) -> a + (a <= b ? b : -b))
;
if (roman.charAt(0) == roman.charAt(1)) {
result += 2 * Enum.valueOf(Numeral.class, roman.substring(0, 1)).weight;
}
return result;
}
 
public static void test(long n) {
System.out.println(n + " = " + encode(n));
System.out.println(encode(n) + " = " + decode(encode(n)));
}
 
public static void main(String[] args) {
LongStream.of(1999, 25, 944).forEach(RomanNumerals::test);
}
}

Output:

1999 = MCMXCIX
MCMXCIX = 1999
25 = XXV
XXV = 25
944 = CMXLIV
CMXLIV = 944

[edit] JavaScript

Works with: Rhino
Works with: SpiderMonkey
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))
}
Output:
MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008

[edit] K

Translation of: J
  romd: {v:1 5 10 50 100 500 1000@"IVXLCDM"?/:x; +/v*_-1^(>':v),0}

Example:

  romd'("MCMXC";"MMVIII";"MDCLXVI")
1990 2008 1666


[edit] Lasso

define br => '\r'
//decode roman
define decodeRoman(roman::string)::integer => {
local(ref = array('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))
local(out = integer)
while(#roman->size) => {
// need to use neset while instead of query expr to utilize loop_abort
while(loop_count <= #ref->size) => {
if(#roman->beginswith(#ref->get(loop_count)->first)) => {
#out += #ref->get(loop_count)->second
#roman->remove(1,#ref->get(loop_count)->first->size)
loop_abort
}
}
}
return #out
}
 
'MCMXC as integer is '+decodeRoman('MCMXC')
br
'MMVIII as integer is '+decodeRoman('MMVIII')
br
'MDCLXVI as integer is '+decodeRoman('MDCLXVI')

[edit] Liberty BASIC

As Fortran & PureBasic.

  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
MCMXCIX = 1999
MDCLXVI = 1666
XXV     = 25
CMLIV   = 954
MMXI    = 2011

[edit]

; 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
Output:
MCMXC:  1990
MDCLXVI:  1666
MMVIII:  2008

[edit] 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" ) )
1990
2008
1666

[edit] Mathematica

FromDigits["MMCDV", "Roman"]

returns 2405

[edit] Mercury

:- module test_roman.
 
:- interface.
 
:- import_module io.
 
:- pred main(io::di, io::uo) is det.
 
:- implementation.
 
:- import_module char.
:- import_module exception.
:- import_module int.
:- import_module list.
:- import_module string.
 
:- type conversion_error ---> not_a_roman_number.
 
:- func build_int(list(char), int, int) = int.
:- func from_roman(string) = int.
:- pred roman_to_int(char::in, int::out) is semidet.
 
from_roman(Roman) = Decimal :-
List = reverse(to_char_list(Roman)),
Decimal = build_int(List, 0, 0).
 
build_int([], LastValue, Accumulator) = LastValue + Accumulator.
build_int([Digit|Rest], LastValue, Accumulator) = Sum :-
( roman_to_int(Digit, Value) ->
( Value < LastValue ->
Sum = build_int(Rest, Value, Accumulator - LastValue)
 ; Sum = build_int(Rest, Value, Accumulator + LastValue) )
 ; throw(not_a_roman_number) ).
 
roman_to_int('I', 1).
roman_to_int('V', 5).
roman_to_int('X', 10).
roman_to_int('L', 50).
roman_to_int('C', 100).
roman_to_int('D', 500).
roman_to_int('M', 1000).
 
main(!IO) :-
command_line_arguments(Args, !IO),
foldl((pred(Arg::in, !.IO::di, !:IO::uo) is det :-
format("%s => %d\n", [s(Arg), i(from_roman(Arg))], !IO)),
Args, !IO).
 
:- end_module test_roman.

[edit] 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
Output:
     MCMXC: 1990
    MMVIII: 2008
   MDCLXVI: 1666

[edit] 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");
;;

[edit] PARI/GP

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
};

[edit] 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);
Output:
MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008

[edit] Perl 6

A non-validating version:

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>;
Output:
MCMXC => 1990
MDCLXVI => 1666
MMVIII => 2008

A validating version. Also handles older forms such as 'IIXX' and "IIII".

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>;
Output:
MCMXC => 1990
mdclxvi => 1666
MMViii => 2008
IIXX => 18
ILL => NaN

[edit] 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)) ) )

Test:

: (roman2decimal "MCMXC")
-> 1990

: (roman2decimal "MMVIII")
-> 2008

: (roman2decimal "MDCLXVI")
-> 1666

[edit] PHP

<?php
/**
* @author Elad Yosifon
*/

$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";
}
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

[edit] 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;
 
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 

[edit] 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;
 

[edit] 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).

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

[edit] 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
Output:
1999
1666
25
954
2011

[edit] 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) )
Output:
MCMXC 1990
MMVIII 2008
MDCLXVI 1666

Another version, which I believe has clearer logic:

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
"""

[edit] 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)

[edit] REXX

[edit] version 1

Translation of: NetRexx
Works with: Regina
Works with: ooRexx
/* 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
Output:
     MCMXC: 1990
    MMVIII: 2008
   MDCLXVI: 1666

[edit] 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

This version won't handle:

  • Roman numbers like   IIXX
  • the   j   and   u   numerals
  • (deep) parenthesis type Roman numbers
/*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
#=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). */

[edit] 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;   (check almost any old clock or "dialed" wristwatch that has Roman numerals).

/*REXX program to convert  Roman numerals ──► Arabic numerals (numbers).*/
numeric digits 1000 /*so we can handle the big nums. */
parse arg z /*get any optional argument(s). */
if z='' then z='MCMXC mmviii IIXX LU MDCLXVI MDWLXVI ((mmm)) [[[[[D]]]]]'
 
do j=1 for words(z); y=word(z,j) /*process each Roman numeral. */
say right(y,20)':' rom2dec(y) /*show original & decimal version*/
end /*j*/
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. */

output when using the default input:

               MCMXC: 1990
              mmviii: 2008
                IIXX: 18
                  LU: 55
             MDCLXVI: 1666
             MDWLXVI: ***error!*** invalid Roman numeral: W
             ((mmm)): 3000000000
         [[[[[D]]]]]: 500000000000000000

[edit] Ruby

def fromRoman(roman)
r = roman.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.slice!(0,len)
end
n
end
 
[ "MCMXC", "MMVIII", "MDCLXVI" ].each {|r| p r => fromRoman(r)}
Output:
{"MCMXC"=>1990}
{"MMVIII"=>2008}
{"MDCLXVI"=>1666}

[edit] Run BASIC

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

[edit] Rust

struct RomanNumeral {
symbol: &'static str,
value: uint
}
 
static NUMERALS: [RomanNumeral, ..13] = [
RomanNumeral {symbol: "M", value: 1000},
RomanNumeral {symbol: "CM", value: 900},
RomanNumeral {symbol: "D", value: 500},
RomanNumeral {symbol: "CD", value: 400},
RomanNumeral {symbol: "C", value: 100},
RomanNumeral {symbol: "XC", value: 90},
RomanNumeral {symbol: "L", value: 50},
RomanNumeral {symbol: "XL", value: 40},
RomanNumeral {symbol: "X", value: 10},
RomanNumeral {symbol: "IX", value: 9},
RomanNumeral {symbol: "V", value: 5},
RomanNumeral {symbol: "IV", value: 4},
RomanNumeral {symbol: "I", value: 1}
];
 
fn to_hindu(roman: &str) -> uint {
for numeral in NUMERALS.iter() {
if roman.starts_with(numeral.symbol) {
return numeral.value + to_hindu(roman.slice_from(numeral.symbol.len()));
}
}
 
return 0;
}
 
fn main() {
let roms = ["MMXIV", "MCMXCIX", "XXV", "MDCLXVI", "MMMDCCCLXXXVIII"];
for r in roms.iter() {
println!("{:s} = {:u}", *r, to_hindu(*r));
}
}
Output:
MMXIV = 2014
MCMXCIX = 1999
XXV = 25
MDCLXVI = 1666
MMMDCCCLXXXVIII = 3888

[edit] 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")
Output:
MCMXC => 1990
MMVIII => 2008
MDCLXVI => 1666

[edit] 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;

Original source: [1]

Output:
1990
2008
1666

[edit] 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
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:

*   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

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.

*   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

[edit] 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:

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}
}

Demonstrating:

foreach r {MCMXC MDCLXVI MMVIII} {
puts "$r\t-> [fromRoman $r]"
}
Output:
MCMXC	-> 1990
MDCLXVI	-> 1666
MMVIII	-> 2008

[edit] TI-83 BASIC

Using the Rom‣Dec function "real(21," from Omnicalc.

PROGRAM:ROM2DEC
:Input Str1
:Disp real(21,Str1)

Using TI-83 BASIC

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

[edit] TUSCRIPT

$$ MODE TUSCRIPT
LOOP roman_number="MCMXC'MMVIII'MDCLXVI"
arab_number=DECODE (roman_number,ROMAN)
PRINT "Roman number ",roman_number," equals ", arab_number
ENDLOOP
Output:
Roman number MCMXC equals 1990
Roman number MMVIII equals 2008
Roman number MDCLXVI equals 1666

[edit] UNIX Shell

 
#!/bin/bash
 
roman_to_dec() {
local rnum=$1
local n=0
local prev=0
 
for ((i=${#rnum}-1;i>=0;i--))
do
case "${rnum:$i:1}" in
M) a=1000 ;;
D) a=500 ;;
C) a=100 ;;
L) a=50 ;;
X) a=10 ;;
V) a=5 ;;
I) a=1 ;;
esac
 
if [[ $a -lt $prev ]]
then
let n-=a
else
let n+=a
fi
 
prev=$a
done
 
echo "$rnum = $n"
}
 
roman_to_dec MCMXC
roman_to_dec MMVIII
roman_to_dec MDCLXVI
 
 
#!/bin/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
}
 
parseroman MCMXC
parseroman MMVIII
parseroman MDCLXVI
 

[edit] Vedit macro language

// 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
Output:
iv	=     4
xii	=    12
MDCLXVI	=  1666
MCMXC	=  1990
MMXI	=  2011

[edit] 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);
]

Output:

1990
2008
1666

[edit] zkl

var romans = L(
L("M", 1000), L("CM", 900), L("D", 500), L("CD", 400), L("C", 100),
L("XC", 90), L("L", 50), L("XL", 40), L("X", 10), L("IX", 9),
L("V", 5), L("IV", 4), L("I", 1));
fcn toArabic(romanNumber){ // romanNumber needs to be upper case
if (not RegExp("^[CDILMVX]+$").matches(romanNumber))
throw(Exception.ValueError("Not a Roman number: %s".fmt(romanNumber)));
reg value = 0;
foreach R,N in (romans){
while (0 == romanNumber.find(R)){
value += N;
romanNumber = romanNumber[R.len(),*];
}
}
return(value);
}
toArabic("MCMXC")   //-->1990
toArabic("MMVIII")  //-->2008
toArabic("MDCLXVI") //-->1666
Personal tools
Namespaces

Variants
Actions
Community
Explore
Misc
Toolbox