Roman numerals

From Rosetta Code

Jump to: navigation, search
Roman numerals is a programming task. Visitors like you are encouraged to solve it according to the task description, using any language they may happen to know.
Add to BlogMarksAdd to del.icio.usAdd to diggAdd to NewsvineAdd to redditAdd to Slashdot

Create a function taking a positive integer as its parameter and returning a string containing the Roman Numeral representation of that integer.

Modern Roman numerals are written by expressing each digit separately starting with the left most digit and skipping any digit with a value of zero. In Roman numerals 1990 is rendered: 1000=M, 900=CM, 90=XC; resulting in MCMXC. 2008 is written as 2000=MM, 8=VIII; or MMVIII. 1666 uses each Roman symbol in descending order: MDCLXVI.

Contents

[edit] Ada

with Ada.Text_IO;  use Ada.Text_IO;
 
procedure Roman_Numeral_Test is
function To_Roman (Number : Positive) return String is
subtype Digit is Integer range 0..9;
function Roman (Figure : Digit; I, V, X : Character) return String is
begin
case Figure is
when 0 => return "";
when 1 => return "" & I;
when 2 => return I & I;
when 3 => return I & I & I;
when 4 => return I & V;
when 5 => return "" & V;
when 6 => return V & I;
when 7 => return V & I & I;
when 8 => return V & I & I & I;
when 9 => return I & X;
end case;
end Roman;
begin
pragma Assert (Number >= 1 and Number < 4000);
return
Roman (Number / 1000, 'M', ' ', ' ') &
Roman (Number / 100 mod 10, 'C', 'D', 'M') &
Roman (Number / 10 mod 10, 'X', 'L', 'C') &
Roman (Number mod 10, 'I', 'V', 'X');
end To_Roman;
begin
Put_Line (To_Roman (1999));
Put_Line (To_Roman (25));
Put_Line (To_Roman (944));
end Roman_Numeral_Test;

Output:

MCMXCIX
XXV
CMXLIV

[edit] ALGOL 68

Works with: ALGOL 68 version Standard - no extensions to language used Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386 Works with: ELLA ALGOL 68 version Any (with appropriate job cards AND formatted transput statements removed) - tested with release 1.8.8d.fc9.i386

[]CHAR roman =        "MDCLXVmdclxvi"; # UPPERCASE for thousands #
[]CHAR adjust roman = "CCXXmmccxxii";
[]INT arabic = (1000000, 500000, 100000, 50000, 10000, 5000, 1000, 500, 100, 50, 10, 5, 1);
[]INT adjust arabic = (100000, 100000, 10000, 10000, 1000, 1000, 100, 100, 10, 10, 1, 1, 0);
 
PROC arabic to roman = (INT dclxvi)STRING: (
INT in := dclxvi; # 666 #
STRING out := "";
FOR scale TO UPB roman WHILE in /= 0 DO
INT multiples = in OVER arabic[scale];
in -:= arabic[scale] * multiples;
out +:= roman[scale] * multiples;
IF in >= -adjust arabic[scale] + arabic[scale] THEN
in -:= -adjust arabic[scale] + arabic[scale];
out +:= adjust roman[scale] + roman[scale]
FI
OD;
out
);
 
main:(
[]INT test = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,25,30,40,50,60,69,70,
80,90,99,100,200,300,400,500,600,666,700,800,900,1000,1009,1444,1666,1945,1997,1999,
2000,2008,2500,3000,4000,4999,5000,6666,10000,50000,100000,500000,1000000,max int);
FOR key TO UPB test DO
INT val = test[key];
print((val, " - ", arabic to roman(val), new line))
OD
)

Output (last example is manually wrapped):

         +1 - i
         +2 - ii
         +3 - iii
         +4 - iv
         +5 - v
         +6 - vi
         +7 - vii
         +8 - viii
         +9 - ix
        +10 - x
        +11 - xi
        +12 - xii
        +13 - xiii
        +14 - xiv
        +15 - xv
        +16 - xvi
        +17 - xvii
        +18 - xviii
        +19 - xix
        +20 - xx
        +25 - xxv
        +30 - xxx
        +40 - xl
        +50 - l
        +60 - lx
        +69 - lxix
        +70 - lxx
        +80 - lxxx
        +90 - xc
        +99 - xcix
       +100 - c
       +200 - cc
       +300 - ccc
       +400 - cd
       +500 - d
       +600 - dc
       +666 - dclxvi
       +700 - dcc
       +800 - dccc
       +900 - cm
      +1000 - m
      +1009 - mix
      +1444 - mcdxliv
      +1666 - mdclxvi
      +1945 - mcmxlv
      +1997 - mcmxcvii
      +1999 - mcmxcix
      +2000 - mm
      +2008 - mmviii
      +2500 - mmd
      +3000 - mmm
      +4000 - mV
      +4999 - mVcmxcix
      +5000 - V
      +6666 - Vmdclxvi
     +10000 - X
     +50000 - L
    +100000 - C
    +500000 - D
   +1000000 - M
+2147483647 - MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
              MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMCDLXXXmmmdcxlvii

[edit] AutoHotkey

Translated from C++ example

MsgBox % stor(444)
 
stor(value)
{
romans = M,CM,D,CD,C,XC,L,XL,X,IX,V,IV,I
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
Loop, Parse, romans, `,
{
While, value >= %A_LoopField%
{
result .= A_LoopField
value := value - (%A_LoopField%)
}
}
Return result . "O"
}

[edit] AWK

Some lines in this example are too long (more than 80 characters). Please fix the code if it's possible and remove this message.

Translation of: Tcl

To cram this into an AWK one-liner is a bit of a stretch, but here goes:

$ awk 'func u(v,n){while(i>=v){r=r n;i-=v}}{i=$1;r="";u(1000,"M");u(900,"CM");u(500,"D");u(400,"CD");u(100,"C");u(90,"XC");u(50,"L");u(40,"XL");u(10,"X");u(9,"IX");u(5,"V");u(4,"IV");u(1,"I");print r}'
2009
MMIX
1999
MCMXCIX

[edit] BASIC

Works with: FreeBASIC

 
DIM SHARED arabic(0 TO 12) AS Integer => {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }
DIM SHARED roman(0 TO 12) AS String*2 => {"M", "CM", "D","CD", "C","XC","L","XL","X","IX","V","IV","I"}
 
FUNCTION toRoman(value AS Integer) AS String
DIM i AS Integer
DIM result AS String
 
FOR i = 0 TO 12
DO WHILE value >= arabic(i)
result = result + roman(i)
value = value - arabic(i)
LOOP
NEXT i
toRoman = result
END FUNCTION
 
'Testing
PRINT "2009 = "; toRoman(2009)
PRINT "1666 = "; toRoman(1666)
PRINT "3888 = "; toRoman(3888)
 

Output

2009 = MMIX
1666 = MDCLXVI
3888 = MMMDCCCLXXXVIII

[edit] C

#include <stdlib.h>
#include <stdio.h>
 
void roman(char *s, unsigned n)
/* Writes the Roman numeral representing n into the buffer s.
Handles up to n = 3999. Since C doesn't have exceptions, n = 0
causes the whole program to exit unsuccessfully. s should be
have room for at least 16 characters, including the trailing
null. */

{if (n == 0)
{puts("Roman numeral for zero requested.");
exit(EXIT_FAILURE);}
 
#define digit(loop, num, c) \
loop (n >= num) \
{*(s++) = c; \
n -= num;}

#define digits(loop, num, c1, c2) \
loop (n >= num) \
{*(s++) = c1; \
*(s++) = c2; \
n -= num;}

 
digit ( while, 1000, 'M' )
digits ( if, 900, 'C', 'M' )
digit ( if, 500, 'D' )
digits ( if, 400, 'C', 'D' )
digit ( while, 100, 'C' )
digits ( if, 90, 'X', 'C' )
digit ( if, 50, 'L' )
digits ( if, 40, 'X', 'L' )
digit ( while, 10, 'X' )
digits ( if, 9, 'I', 'X' )
digit ( if, 5, 'V' )
digits ( if, 4, 'I', 'V' )
digit ( while, 1, 'I' )
 
#undef digit
#undef digits
 
*s = 0;}
 
int main(void)
{char buffer[16];
for (int i = 1 ; i < 4000 ; ++i)
{roman(buffer, i);
printf("%4d: %s\n", i, buffer);}
return 1;}
An alternative version which builds the string backwards.
char *ToRoman(int num, char *buf, int buflen)
{
static const char *romanDgts = "ivxlcdmVXLCDM_";
char *roman = buf + buflen;
int rdix, r, v;
*--roman = '\0'; /* null terminate return string */
if (num >= 4000000) {
printf("Number Too Big.\n");
return NULL;
}
for (rdix = 0; rdix < strlen(romanDgts); rdix += 2) {
if (num == 0) break;
v = (num % 10) / 5;
r = num % 5;
num = num / 10;
if (r == 4) {
if (roman < buf+2) {
printf("Buffer too small.");
return NULL;
}
*--roman = romanDgts[rdix+1+v];
*--roman = romanDgts[rdix];
}
else {
if (roman < buf+r+v) {
printf("Buffer too small.");
return NULL;
}
while(r-- > 0) {
*--roman = romanDgts[rdix];
}
if (v==1) {
*--roman = romanDgts[rdix+1];
}
}
}
return roman;
}

[edit] C#

 class Program
{
static int[] nums = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
static string[] rum = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
 
static void Main(string[] args)
{
string value = "";
int number = int.Parse(Console.ReadLine());
for (int i = 0; i < nums.Length; i++)
{
while (number >= nums[i])
{
number -= nums[i];
value += rum[i];
}
if (number == 0)
break;
}
Console.WriteLine(value);
Console.ReadLine();
}
}

[edit] C++

#include <iostream>
#include <string>
 
std::string to_roman(int value)
{
struct romandata_t { int value; char const* numeral; };
static romandata_t const romandata[] =
{ 1000, "M",
900, "CM",
500, "D",
400, "CD",
100, "C",
90, "XC",
50, "L",
40, "XL",
10, "X",
9, "IX",
5, "V",
4, "IV",
1, "I",
0, 0 }; // end marker
 
std::string result;
for (romandata_t const* current = romandata; current->value > 0; ++current)
{
while (value >= current->value)
{
result += current->numeral;
value -= current->value;
}
}
return result;
}
 
int main()
{
for (int i = 1; i <= 4000; ++i)
{
std::cout << to_roman(i) << std::endl;
}
}

[edit] Common Lisp

(defun roman-numeral (n)
(format nil "~@R" n))

[edit] Clojure

 
(def arabic-roman-map
{1 "I", 5 "V",
10 "X", 50 "L",
100 "C", 500 "D",
1000 "M",
4 "IV", 9 "IX",
40 "XL", 90 "XC",
400 "CD", 900 "CM" })
 
(def arabic-roman-map-sorted-keys
(sort (keys arabic-roman-map)))
 
(defn find-value-in-coll
[coll k]
(let [aval (find coll k)]
(if (nil? aval) "" (val aval))))
 
(defn to-roman
[result n]
(let
[closest-key-for-n (last (filter #(> n %) arabic-roman-map-sorted-keys))
roman-value-for-n (find-value-in-coll arabic-roman-map n)
roman-value-for-closet-to-n (find-value-in-coll arabic-roman-map
closest-key-for-n)]
(if (or (<= n 0)(contains? arabic-roman-map n))
(conj result roman-value-for-n)
(recur (conj result roman-value-for-closet-to-n)
(- n closest-key-for-n)))))
 
Usage: >(to-roman [] 1999)
result: ["M" "CM" "XC" "IX"]
 
 

[edit] D

This implementation in generally follows the rules implied by Modern Roman numerals, with some irregularity depend on whether numerals larger than M(1000) is used, eg. 4000 is converted to MV' if V' is used, MMMM if not.

module roman ;
import std.stdio ;
 
const string[] Roman = ["V","X","L","C","D","M","I"] ;
const int RLen = Roman.length - 1 ;
const int[][] RDigit =
[[0],[0,0],[0,0,0],[0,1],[1],[1,0],[1,0,0],[1,0,0,0],[0,2],[0,0,0,0]] ;
const string[] Power = ["", "'","\"","`","~","^","#"] ; // arbitary _power_ symbols, or
// Power = ["1","2","3","4","5","6","7"] ; // for easier further processing
const int[][] Shift = [[0,0,0],[-1,0,0]] ;
 
string romanPart(int n, int part, bool extented) {
if (n == 0) return "" ;
int[3] b ;
b[1] = (2 * part) % RLen ;
b[0] = part == 0 ? RLen : (RLen + b[1] - 1) % RLen ;
b[2] = b[1] + 1 ;
int power = part / 3 ;
int[] shift = Shift[ b[1] == 0 && part != 0 ? 1 : 0] ;
int[] Digit = !extented && n == 4 && part == 3 ? RDigit[$-1] : RDigit[n-1] ;
string res ;
foreach(inx ; Digit)
res ~= Roman[b[inx]] ~ Power[power + shift[inx]] ;
return res ;
}
string toRoman(long n, bool extented = true) {
if(n < 0) throw new Exception("No negative Roman Numeral") ;
if(n == 0) return "" ;
if(!extented && n >= 5000) throw new Exception("Only smaller than 5000 allowed") ;
string romans ;
int part = 0 ;
while (n > 0) {
long m = n / 10 ;
romans = romanPart(n - m*10, part, extented) ~ romans ;
n = m ;
part++ ;
}
return romans ;
}
void main() {
auto test = [1L,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,25,30,40,50,60,69,70,
80,90,99,100,200,300,400,500,600,666,700,800,900,1000,1009,1444,1666,1945,1997, 1999,
2000,2008,2500,3000,4000,4999,5000,6666,10000,50000,100000,500000,1000000,long.max] ;
foreach(x ; test)
writefln("%20s - %s", x, toRoman(x)) ;
}

[edit] Erlang

Translation of: OCaml

-module(roman).
-export([to_roman/1]).
 
to_roman(0) -> [];
to_roman(X) when X >= 1000 -> [$M | to_roman(X - 1000)];
to_roman(X) when X >= 100 ->
digit(X div 100, $C, $D, $M) ++ to_roman(X rem 100);
to_roman(X) when X >= 10 ->
digit(X div 10, $X, $L, $C) ++ to_roman(X rem 10);
to_roman(X) when X >= 1 -> digit(X, $I, $V, $X).
 
digit(1, X, _, _) -> [X];
digit(2, X, _, _) -> [X, X];
digit(3, X, _, _) -> [X, X, X];
digit(4, X, Y, _) -> [X, Y];
digit(5, _, Y, _) -> [Y];
digit(6, X, Y, _) -> [Y, X];
digit(7, X, Y, _) -> [Y, X, X];
digit(8, X, Y, _) -> [Y, X, X, X];
digit(9, X, _, Z) -> [X, Z].

sample:

1> c(roman).            
{ok,roman}
2> roman:to_roman(1999).
"MCMXCIX"
3> roman:to_roman(25).  
"XXV"
4> roman:to_roman(944).
"CMXLIV"

[edit] Factor

A roman numeral library ships with Factor.

USE: roman
( scratchpad ) 3333 >roman .
"mmmcccxxxiii"

Parts of the implementation:

CONSTANT: roman-digits
{ "m" "cm" "d" "cd" "c" "xc" "l" "xl" "x" "ix" "v" "iv" "i" }
 
CONSTANT: roman-values
{ 1000 900 500 400 100 90 50 40 10 9 5 4 1 }
 
ERROR: roman-range-error n ;
 
: roman-range-check ( n -- n )
dup 1 10000 between? [ roman-range-error ] unless ;
 
: >roman ( n -- str )
roman-range-check
roman-values roman-digits [
[ /mod swap ] dip <repetition> concat
] 2map "" concat-as nip ;

[edit] FALSE

^$." "
[$999>][1000- "M"]#
$899> [ 900-"CM"]?
$499> [ 500- "D"]?
$399> [ 400-"CD"]?
[$ 99>][ 100- "C"]#
$ 89> [ 90-"XC"]?
$ 49> [ 50- "L"]?
$ 39> [ 40-"XL"]?
[$ 9>][ 10- "X"]#
$ 8> [ 9-"IX"]?
$ 4> [ 5- "V"]?
$ 3> [ 4-"IV"]?
[$ ][ 1- "I"]#%

[edit] Fan

**
** converts a number to its roman numeral representation
**
class RomanNumerals
{
 
private Str digit(Str x, Str y, Str z, Int i)
{
switch (i)
{
case 1: return x
case 2: return x+x
case 3: return x+x+x
case 4: return x+y
case 5: return y
case 6: return y+x
case 7: return y+x+x
case 8: return y+x+x+x
case 9: return x+z
}
return ""
}
 
Str toRoman(Int i)
{
if (i>=1000) { return "M" + toRoman(i-1000) }
if (i>=100) { return digit("C", "D", "M", i/100) + toRoman(i%100) }
if (i>=10) { return digit("X", "L", "C", i/10) + toRoman(i%10) }
if (i>=1) { return digit("I", "V", "X", i) }
return ""
}
 
Void main()
{
2000.times |i| { echo("$i = ${toRoman(i)}") }
}
 
}

[edit] Forth

: vector create ( n -- ) 0 do , loop
does> ( n -- ) swap cells + @ execute ;
 
\ these are ( numerals -- numerals )
: .I dup c@ emit ;
: .V dup 1 + c@ emit ;
: .X dup 2 + c@ emit ;
 
\ these are ( numerals -- )
:noname .I .X drop ;
:noname .V .I .I .I drop ;
:noname .V .I .I drop ;
:noname .V .I drop ;
:noname .V drop ;
:noname .I .V drop ;
:noname .I .I .I drop ;
:noname .I .I drop ;
:noname .I drop ;
' drop \ 0: no output
10 vector .digit
 
: roman-rec ( numerals n -- )
10 /mod dup if >r over 2 + r> recurse else drop then .digit ;
 
: .roman ( n -- )
dup 0 4000 within 0= if ." EX LIMITO!" exit then
s" IVXLCDM" drop swap roman-rec ;

[edit] Fortran

Works with: Fortran version 90 and later

program roman_numerals
 
implicit none
 
write (*, '(a)') roman (2009)
write (*, '(a)') roman (1666)
write (*, '(a)') roman (3888)
 
contains
 
function roman (n) result (r)
 
implicit none
integer, intent (in) :: n
integer, parameter :: d_max = 13
integer :: d
integer :: m
integer :: m_div
character (32) :: r
integer, dimension (d_max), parameter :: d_dec = &
& (/1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1/)
character (32), dimension (d_max), parameter :: d_rom = &
& (/'M ', 'CM', 'D ', 'CD', 'C ', 'XC', 'L ', 'XL', 'X ', 'IX', 'V ', 'IV', 'I '/)
 
r = ''
m = n
do d = 1, d_max
m_div = m / d_dec (d)
r = trim (r) // repeat (trim (d_rom (d)), m_div)
m = m - d_dec (d) * m_div
end do
 
end function roman
 
end program roman_numerals

Output:

 MMIX
 MDCLXVI
 MMMDCCCLXXXVIII

[edit] Haskell

With an explicit decimal digit representation list:

digit x y z k = 
[[x],[x,x],[x,x,x],[x,y],[y],[y,x],[y,x,x],[y,x,x,x],[x,z]] !!
(fromInteger k - 1)
 
toRoman :: Integer -> String
toRoman 0 = ""
toRoman x | x < 0 = error "Negative roman numeral"
toRoman x | x >= 1000 = 'M' : toRoman (x - 1000)
toRoman x | x >= 100 = digit 'C' 'D' 'M' q ++ toRoman r where
(q,r) = x `divMod` 100
toRoman x | x >= 10 = digit 'X' 'L' 'C' q ++ toRoman r where
(q,r) = x `divMod` 10
toRoman x = digit 'I' 'V' 'X' x

Output:

*Main> map toRoman [1999,25,944]
["MCMXCIX","XXV","CMXLIV"]

[edit] J

rfd obtains Roman numerals from decimals, and dfr decimals from Roman numerals.

dfr=: 3 : 0
i=. 'IVXLCDM' i. y
d=. i{1 5 10 50 100 500 1000
+/d*_1^i<}.i,_1
)
 
r100 =. <;._1 ' C CC CCC CD D DC DCC DCCC CM'
r10 =. <;._1 ' X XX XXX XL L LX LXX LXXX XC'
r1 =. <;._1 ' I II III IV V VI VII VIII IX'
R1000=: , r100 ,&.>/ r10 ,&.>/ r1
 
rfd=: 3 : 0
('M'$~<.y%1000),R1000{::~1000|y
)

Copied, with permission, from the J Wiki. Examples of use will be found there.

[edit] Java

Translation of: Ada

The helper function copies is added since Java does not support String multiplication. The conversion function returns null for non-positive numbers, since Java does not have unsigned primitives.

public class RN{
public static void main(String args[]){
System.out.println(roman(1999));
System.out.println(roman(25));
System.out.println(roman(954));
}
public static String roman(long n){
if(n < 1) return null;
String result = "";
if(n >= 1000){
result+= (copies("M",(n / 1000)));
n%= 1000;
}
if(n >= 900){
result+= "CM";
n%= 900;
}
if(n >= 500){
result+= "D";
n%= 500;
}
if(n >= 400){
result+= "CD";
n%= 400;
}
if(n >= 100){
result+= (copies("C",(n / 100)));
n%= 100;
}
if(n >= 90){
result+= "XC";
n%= 90;
}
if(n >= 50){
result+= "L";
n%= 50;
}
if(n >= 40){
result+= "XL";
n%= 40;
}
if(n >= 10){
result+= (copies("X",(n / 10)));
n%= 10;
}
if(n == 9){
result+= "IX";
n= 0;
}
if(n >= 5){
result+= "V";
n%= 5;
}
if(n == 4){
result+= "IV";
n= 0;
}
result+= (copies("I",n));
return result;
}
 
public static String copies(String a, int n){
String result = "";
for(int i= 0;i < n;i++,result+= a);
return result;
}
}

Output:

MCMXCIX
XXV
CMXLIV

[edit] JavaScript

Translation of: Tcl

var roman = {
map: [
1000, 'M', 900, 'CM', 500, 'D', 400, 'CD', 100, 'C', 90, 'XC',
50, 'L', 40, 'XL', 10, 'X', 9, 'IX', 5, 'V', 4, 'IV', 1, 'I',
],
int_to_roman: function(n) {
var value = '';
for (var idx = 0; n > 0 && idx < this.map.length; idx += 2) {
while (n >= this.map[idx]) {
value += this.map[idx + 1];
n -= this.map[idx];
}
}
return value;
}
}
 
roman.int_to_roman(1999); // "MCMXCIX"

[edit] LaTeX

The macro \Roman is defined for uppercase roman numeral, accepting as argument a name of an existing counter.

\documentclass{article}
\begin{document}
\newcounter{currentyear}\setcounter{currentyear}{\year}
Anno Domini \Roman{currentyear}
\end{document}

[edit] Logo

make "roman.rules [
[1000 M] [900 CM] [500 D] [400 CD]
[ 100 C] [ 90 XC] [ 50 L] [ 40 XL]
[ 10 X] [ 9 IX] [ 5 V] [ 4 IV]
[ 1 I]
]
 
to roman :n [:rules :roman.rules] [:acc "||]
if empty? :rules [output :acc]
if :n < first first :rules [output (roman :n bf :rules :acc)]
output (roman :n - first first :rules  :rules word :acc last first :rules)
end

Works with: UCB Logo

make "patterns [[?] [? ?] [? ? ?] [? ?2] [?2] [?2 ?] [?2 ? ?] [?2 ? ? ?] [? ?3]]
 
to digit :d :numerals
if :d = 0 [output "||]
output apply (sentence "\( "word (item :d :patterns) "\)) :numerals
end
to digits :n :numerals
output word ifelse :n < 10 ["||] [digits int :n/10 bf bf :numerals] ~
digit modulo :n 10 :numerals
end
to roman :n
if or :n < 0 :n >= 4000 [output [EX MODVS!]]
output digits :n [I V X L C D M]
end
 
print roman 1999  ; MCMXCIX
print roman 25  ; XXV
print roman 944  ; CMXLIV


[edit] Lua

romans = {
{1000, "M"},
{900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"},
{90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"},
{9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"} }
 
k = io.read() + 0
for _, v in ipairs(romans) do --note that this is -not- ipairs.
val, let = unpack(v)
while k >= val do
k = k - val
io.write(let)
end
end
print()

[edit] M4

define(`roman',`ifelse(eval($1>=1000),1,`M`'roman(eval($1-1000))',
`ifelse(eval($1>=900),1,`CM`'roman(eval($1-900))',
`ifelse(eval($1>=500),1,`D`'roman(eval($1-500))',
`ifelse(eval($1>=100),1,`C`'roman(eval($1-100))',
`ifelse(eval($1>=90),1,`XC`'roman(eval($1-90))',
`ifelse(eval($1>=50),1,`L`'roman(eval($1-50))',
`ifelse(eval($1>=40),1,`XL`'roman(eval($1-40))',
`ifelse(eval($1>=10),1,`X`'roman(eval($1-10))',
`ifelse(eval($1>=9),1,`IX`'roman(eval($1-9))',
`ifelse(eval($1>=5),1,`V`'roman(eval($1-5))',
`ifelse(eval($1>=4),1,`IV`'roman(eval($1-4))',
`ifelse(eval($1>=1),1,`I`'roman(eval($1-1))'
)')')')')')')')')')')')')dnl
dnl
roman(3675)

Output:

MMMDCLXXV

[edit] Mathematica

Define a custom function that works on positive numbers (RomanForm[0] will not be evaluated):

RomanForm[i_Integer?Positive] := 
Module[{num = i, string = "", value, letters, digits},
digits = {{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100,
"C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9,
"IX"}, {5, "V"}, {4, "IV"}, {1, "I"}};
While[num > 0, {value, letters} =
Which @@ Flatten[{num >= #[[1]], ##} & /@ digits, 1];
num -= value;
string = string <> letters;];
string]

Examples:

RomanForm[4]
RomanForm[99]
RomanForm[1337]
RomanForm[1666]
RomanForm[6889]

gives back:

IV
XCIX
MCCCXXXVII
MDCLXVI
MMMMMMDCCCLXXXIX

[edit] OCaml

With an explicit decimal digit representation list:

let digit x y z = function
1 -> [x]
| 2 -> [x;x]
| 3 -> [x;x;x]
| 4 -> [x;y]
| 5 -> [y]
| 6 -> [y;x]
| 7 -> [y;x;x]
| 8 -> [y;x;x;x]
| 9 -> [x;z]
 
let rec to_roman x =
if x = 0 then []
else if x < 0 then
invalid_arg "Negative roman numeral"
else if x >= 1000 then
'M' :: to_roman (x - 1000)
else if x >= 100 then
digit 'C' 'D' 'M' (x / 100) @ to_roman (x mod 100)
else if x >= 10 then
digit 'X' 'L' 'C' (x / 10) @ to_roman (x mod 10)
else
digit 'I' 'V' 'X' x

Output:

# to_roman 1999;;
- : char list = ['M'; 'C'; 'M'; 'X'; 'C'; 'I'; 'X']
# to_roman 25;;
- : char list = ['X'; 'X'; 'V']
# to_roman 944;;
- : char list = ['C'; 'M'; 'X'; 'L'; 'I'; 'V']

[edit] Oz

Translation of: Haskell

declare
fun {Digit X Y Z K}
unit([X] [X X] [X X X] [X Y] [Y] [Y X] [Y X X] [Y X X X] [X Z])
.K
end
 
fun {ToRoman X}
if X == 0 then ""
elseif X < 0 then raise toRoman(negativeInput X) end
elseif X >= 1000 then "M"#{ToRoman X-1000}
elseif X >= 100 then {Digit &C &D &M X div 100}#{ToRoman X mod 100}
elseif X >= 10 then {Digit &X &L &C X div 10}#{ToRoman X mod 10}
else {Digit &I &V &X X}
end
end
in
{ForAll {Map [1999 25 944] ToRoman} System.showInfo}

[edit] Perl

Works with: Lingua::Romana::Perligata Perligata outputs numbers in Arabic, but the verb come ("beautify") may be used to convert numbers to proper Roman numerals:

per quisque in I tum C conscribementum sic
hoc tum duos multiplicamentum comementum egresso scribe.
cis

[edit] Perl 6

Translation of: Ruby

Works with: Rakudo version #22 "Thousand Oaks"

my %symbols = map {$^v => $^k}, {
I => 1, V => 5, X => 10, L => 50, C => 100, D => 500, M => 1000
}.kv;
 
my @subtractors =
1000, 100, 500, 100, 100, 10, 50, 10, 10, 1, 5, 1, 1, 0;
 
sub roman (Int $n where { $n > 0 }) {
if %symbols{$n} -> $sym { return $sym };
for @subtractors -> $cut, $minus {
$cut < $n
and return %symbols{$cut} ~ roman($n - $cut);
$cut - $minus <= $n
and return %symbols{$minus} ~ roman($n + $minus);
}
}

[edit] PHP

Works with: PHP version 4+ tested in 5.2.12

 
/**
* int2roman
* Convert any positive value of a 32-bit signed integer to its modern roman
* numeral representation. Numerals within parentheses are multiplied by
* 1000. ie. M == 1 000, (M) == 1 000 000, ((M)) == 1 000 000 000
*
* @param number - an integer between 1 and 2147483647
* @return roman numeral representation of number
*/

function int2roman($number)
{
if (!is_int($number) || $number < 1) return false; // ignore negative numbers and zero
 
$integers = array(900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1);
$numerals = array('CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I');
$major = intval($number / 1000) * 1000;
$minor = $number - $major;
$numeral = $leastSig = '';
 
for ($i = 0; $i < sizeof($integers); $i++) {
while ($minor >= $integers[$i]) {
$leastSig .= $numerals[$i];
$minor -= $integers[$i];
}
}
 
if ($number >= 1000 && $number < 40000) {
if ($major >= 10000) {
$numeral .= '(';
while ($major >= 10000) {
$numeral .= 'X';
$major -= 10000;
}
$numeral .= ')';
}
if ($major == 9000) {
$numeral .= 'M(X)';
return $numeral . $leastSig;
}
if ($major == 4000) {
$numeral .= 'M(V)';
return $numeral . $leastSig;
}
if ($major >= 5000) {
$numeral .= '(V)';
$major -= 5000;
}
while ($major >= 1000) {
$numeral .= 'M';
$major -= 1000;
}
}
 
if ($number >= 40000) {
$major = $major/1000;
$numeral .= '(' . int2roman($major) . ')';
}
 
return $numeral . $leastSig;
}
 

[edit] PicoLisp

(de roman (N)
(pack
(make
(mapc
'((C D)
(while (>= N D)
(dec 'N D)
(link C) ) )
'(M CM D CD C XC L XL X IX V IV I)
(1000 900 500 400 100 90 50 40 10 9 5 4 1) ) ) ) )

Output:

: (roman 1009)
-> "MIX"

: (roman 1666)
-> "MDCLXVI"

[edit] Pike

import String;
int main(){
write(int2roman(2009) + "\n");
write(int2roman(1666) + "\n");
write(int2roman(1337) + "\n");
}

[edit] plainTeX

TeX has its own way to convert a number into roman numeral, but it produces lowercase letters; the following macro (and usage example), produce uppercase roman numeral.

\def\upperroman#1{\uppercase\expandafter{\romannumeral#1}}
Anno Domini \upperroman{\year}
\bye

[edit] PL/I

 
/* From Wiki Fortran */
roman: procedure (n) returns(character (32) varying);
declare n fixed binary nonassignable;
declare (d, m) fixed binary;
declare (r, m_div) character (32) varying;
declare d_dec(13) fixed binary static initial
(1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1);
declare d_rom(13) character (2) varying static initial
('M', 'CM', 'D', 'CD', 'C', 'XC', 'L',
'XL', 'X', 'IX', 'V', 'IV', 'I');
r = '';
m = n;
do d = 1 to 13;
m_div = m / d_dec (d);
r = r || copy (d_rom (d), m_div);
m = m - d_dec (d) * m_div;
end;
return (r);
end roman;
 

[edit] PureBasic

#SymbolCount = 12 ;0 based count
DataSection
denominations:
Data.s "M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I" ;0-12
 
denomValues:
Data.i 1000,900,500,400,100,90,50,40,10,9,5,4,1 ;values in decending sequential order
EndDataSection
 
;-setup
Structure romanNumeral
symbol.s
value.i
EndStructure
 
Global Dim refRomanNum.romanNumeral(#SymbolCount)
 
Restore denominations
For i = 0 To #SymbolCount
Read.s refRomanNum(i)\symbol
Next
 
Restore denomValues
For i = 0 To #SymbolCount
Read refRomanNum(i)\value
Next
 
Procedure.s decRoman(n)
;converts a decimal number to a roman numeral
Protected roman$, i
 
For i = 0 To #SymbolCount
Repeat
If n >= refRomanNum(i)\value
roman$ + refRomanNum(i)\symbol
n - refRomanNum(i)\value
Else
Break
EndIf
ForEver
Next
 
ProcedureReturn roman$
EndProcedure
 
If OpenConsole()
 
PrintN(decRoman(1999)) ;MCMXCIX
PrintN(decRoman(1666)) ;MDCLXVI
PrintN(decRoman(25)) ;XXV
PrintN(decRoman(954)) ;CMLIV
 
Print(#CRLF$ + #CRLF$ + "Press ENTER to exit")
Input()
CloseConsole()
EndIf

[edit] Python

roman =        "MDCLXVmdclxvi"; # UPPERCASE for thousands #
adjust_roman = "CCXXmmccxxii";
arabic = (1000000, 500000, 100000, 50000, 10000, 5000, 1000, 500, 100, 50, 10, 5, 1);
adjust_arabic = (100000, 100000, 10000, 10000, 1000, 1000, 100, 100, 10, 10, 1, 1, 0);
 
def arabic_to_roman(dclxvi):
org = dclxvi; # 666 #
out = "";
for scale,arabic_scale in enumerate(arabic):
if org == 0: break
multiples = org / arabic_scale;
org -= arabic_scale * multiples;
out += roman[scale] * multiples;
if org >= -adjust_arabic[scale] + arabic_scale:
org -= -adjust_arabic[scale] + arabic_scale;
out += adjust_roman[scale] + roman[scale]
return out
 
if __name__ == "__main__":
test = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,25,30,40,50,60,69,70,
80,90,99,100,200,300,400,500,600,666,700,800,900,1000,1009,1444,1666,1945,1997,1999,
2000,2008,2500,3000,4000,4999,5000,6666,10000,50000,100000,500000,1000000);
for val in test:
print '%d - %s'%(val, arabic_to_roman(val))
An alternative which uses the divmod() function
romanDgts= 'ivxlcdmVXLCDM_'
 
def ToRoman(num):
namoR = ''
if num >=4000000:
print 'Too Big -'
return '-----'
for rdix in range(0, len(romanDgts), 2):
if num==0: break
num,r = divmod(num,10)
v,r = divmod(r, 5)
if r==4:
namoR += romanDgts[rdix+1+v] + romanDgts[rdix]
else:
namoR += r*romanDgts[rdix] + (romanDgts[rdix+1] if(v==1) else '')
return namoR[-1::-1]

[edit] R

R has a built-in function, as.roman, for conversion to roman numerals. The implementation details are found in utils:::.numeric2roman (see previous link), and utils:::.roman2numeric, for conversion back to arabic decimals.

as.roman(1666)   # MDCLXVI

[edit] REXX

roman: procedure
arg number
 
/* handle only 1 to 3999, else return ? */
if number >= 4000 | number <= 0 then return "?"
 
romans = " M CM D CD C XC L XL X IX V IV I"
arabic = "1000 900 500 400 100 90 50 40 10 9 5 4 1"
 
result = ""
do i = 1 to words(romans)
do while number >= word(arabic,i)
result = result || word(romans,i)
number = number - word(arabic,i)
end
end
return result

[edit] Ruby

Roman numeral generation was used as an example for demonstrating Test Driven Development in Ruby. The solution came to be:

Symbols = { 1=>'I', 5=>'V', 10=>'X', 50=>'L', 100=>'C', 500=>'D', 1000=>'M' }
Subtractors = [ [1000, 100], [500, 100], [100, 10], [50, 10], [10, 1], [5, 1], [1, 0] ]
 
def roman(num)
return Symbols[num] if Symbols.has_key?(num)
Subtractors.each do |cutPoint, subtractor|
return roman(cutPoint) + roman(num - cutPoint) if num > cutPoint
return roman(subtractor) + roman(num + subtractor) if num >= cutPoint - subtractor and num < cutPoint
end
end

[edit] Scala

Works with: Scala version 2.8

val romanDigits = Map(
1 -> "I", 5 -> "V",
10 -> "X", 50 -> "L",
100 -> "C", 500 -> "D",
1000 -> "M",
4 -> "IV", 9 -> "IX",
40 -> "XL", 90 -> "XC",
400 -> "CD", 900 -> "CM")
val romanDigitsKeys = romanDigits.keysIterator.toList sortBy (x => -x)
def toRoman(n: Int): String = romanDigitsKeys find (_ >= n) match {
case Some(key) => romanDigits(key) + toRoman(n - key)
case None => ""
}

Sample:

scala> List(1990, 2008, 1666) map toRoman
res55: List[String] = List(MCMXC, MMVIII, MDCLXVI)

[edit] Scheme

This uses format directives supported in Chez Scheme since v6.9b; YMMV.

(define (to-roman n)
(format "~@r" n))

[edit] Tcl

proc to_roman {i} {
set map {1000 M 900 CM 500 D 400 CD 100 C 90 XC 50 L 40 XL 10 X 9 IX 5 V 4 IV 1 I}
foreach {value roman} $map {
while {$i >= $value} {
append res $roman
incr i -$value
}
}
return $res
}

[edit] SNOBOL4

Adapted from Catspaw SNOBOL Tutorial, Chapter 6

 
* ROMAN(N) - Convert integer N to Roman numeral form.
*
* N must be positive and less than 4000.
*
* An asterisk appears in the result if N >= 4000.
*
* The function fails if N is not an integer.
 
DEFINE('ROMAN(N)UNITS')  :(ROMAN_END)
 
* Get rightmost digit to UNITS and remove it from N.
* Return null result if argument is null.
ROMAN N RPOS(1) LEN(1) . UNITS =  :F(RETURN)
 
* Search for digit, replace with its Roman form.
* Return failing if not a digit.
'0,1I,2II,3III,4IV,5V,6VI,7VII,8VIII,9IX,' UNITS
+ BREAK(',') . UNITS  :F(FRETURN)
 
* Convert rest of N and multiply by 10. Propagate a
* failure return from recursive call back to caller.
ROMAN = REPLACE(ROMAN(N), 'IVXLCDM', 'XLCDM**')
+ UNITS  :S(RETURN) F(FRETURN)
ROMAN_END
 
* Testing
OUTPUT = "1999 = " ROMAN(1999)
OUTPUT = " 24 = " ROMAN(24)
OUTPUT = " 944 = " ROMAN(944)
 
END

Outputs:

1999 = MCMXCIX
  24 = XXIV
 944 = CMXLIV

[edit] Ursala

The algorithm is to implement the subtractive principle by string substitution only after constucting the numeral from successive remainders. The order among the substitutions matters. For example, occurrences of DCCCC must be replaced by CM before any occurrences of CCCC are replaced by CD. The substitution operator (%=) is helpful here.

#import nat
 
roman =
 
-+
'IIII'%='IV'+ 'VIIII'%='IX'+ 'XXXX'%='XL'+ 'LXXXX'%='XC'+ 'CCCC'%='CD'+ 'DCCCC'%='CM',
~&plrDlSPSL/'MDCLXVI'+ iota*+ +^|(^|C/~&,\/division)@rlX=>~&iNC <1000,500,100,50,10,5>+-

This test program applies the function to each member of a list of numbers.

#show+
 
test = roman* <1990,2008,1,2,64,124,1666,10001>

output:

MCMXC
MMVIII
I
II
LXIV
CXXIV
MDCLXVI
MMMMMMMMMMI

[edit] Vedit macro language

do {
#1 = Get_Num("Number to convert: ")
Call("ROMAN_NUMBER")
Reg_Type(1) Message("\n")
} while (Reg_Size(1))
Return
 
// Convert numeric value into Roman number
// #1 = number to convert; on return: T-reg(1) = Roman number
//
:ROMAN_NUMBER:
Reg_Empty(1) // @1 = Results (Roman number)
if (#1 < 1) { Return } // non-positive numbers return empty string
 
Buf_Switch(Buf_Free)
Ins_Text("M1000,CM900,D500,CD400,C100,XC90,L50,XL40,X10,IX9,V5,IV4,I1")
 
BOF
Repeat(ALL) {
Search("|A|[|A]", ADVANCE+ERRBREAK) // get next item from conversion list
Reg_Copy_Block(20, CP-Chars_Matched, CP) // @20 = Letter(s) to be inserted
#11 = Num_Eval() // #11 = magnitude (1000...1)
while (#1 >= #11) {
Reg_Set(1, @20, APPEND)
#1 -= #11
}
}
Buf_Quit(OK)
Return
Personal tools
Google AdSense