Roman numerals/Encode
Create a function taking a positive integer as its parameter and returning a string containing the Roman Numeral representation of that integer.
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
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.
Ada
<lang 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; </lang> Output:
MCMXCIX XXV CMXLIV
ALGOL 68
<lang algol> []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
)</lang> Output:
+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 - MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMCDLXXXmmmdcxlvii
AutoHotkey
Translated from C++ example <lang AutoHotkey> 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"
} </lang>
AWK
To cram this into an AWK one-liner is a bit of a stretch, but here goes: <lang awk> $ 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 </lang>
BASIC
<lang 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) </lang>
Output
2009 = MMIX 1666 = MDCLXVI 3888 = MMMDCCCLXXXVIII
C
<lang 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;}</lang>
An alternative version which builds the string backwards. <lang c>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;
}</lang>
C++
<lang cpp>
- 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; }
} </lang>
Common Lisp
(defun roman-numeral (n) (format nil "~@R" n))
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. <lang d>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)) ;
}</lang>
Factor
A roman numeral library ships with Factor.
USE: roman ( scratchpad ) 3333 >roman . "mmmcccxxxiii"
: roman-digits ( -- seq ) { "m" "cm" "d" "cd" "c" "xc" "l" "xl" "x" "ix" "v" "iv" "i" } ; : roman-values ( -- seq ) { 1000 900 500 400 100 90 50 40 10 9 5 4 1 } ; ERROR: roman-range-error n ; : roman-range-check ( n -- ) dup 1 3999 between? [ drop ] [ roman-range-error ] if ; : (>roman) ( n -- ) roman-values roman-digits [ [ /mod swap ] dip <repetition> concat % ] 2each drop ; : >roman ( n -- str ) dup roman-range-check [ (>roman) ] "" make ;
FALSE
<lang 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"]#% </lang>
Fan
<lang 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)}") } }
} </lang>
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 ;
Fortran
<lang fortran>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 </lang>
Output:
MMIX MDCLXVI MMMDCCCLXXXVIII
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"]
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.
Java
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. <lang java>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; } }</lang> Output:
MCMXCIX XXV CMXLIV
LaTeX
The macro \Roman
is defined for uppercase roman numeral, accepting as argument a name of an existing counter.
<lang latex>\documentclass{article} \begin{document} \newcounter{currentyear}\setcounter{currentyear}{\year} Anno Domini \Roman{currentyear} \end{document}</lang>
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
M4
<lang 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) </lang>
Output:
MMMDCLXXV
Mathematica
Define a custom function that works on positive numbers (RomanForm[0] will not be evaluated): <lang Mathematica>
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 ]
</lang> Examples: <lang Mathematica>
RomanForm[4] RomanForm[99] RomanForm[1337] RomanForm[1666] RomanForm[6889]
</lang> gives back: <lang Mathematica>
IV XCIX MCCCXXXVII MDCLXVI MMMMMMDCCCLXXXIX
</lang>
OCaml
With an explicit decimal digit representation list:
<lang ocaml> 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
</lang>
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']
Perl
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
Perl 6
<lang perl6>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); }
}</lang>
Plain TeX
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.
<lang tex>\def\upperroman#1{\uppercase\expandafter{\romannumeral#1}} Anno Domini \upperroman{\year} \bye</lang>
Python
<lang 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))
</lang> An alternative which uses the divmod() function <lang python>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]</lang>
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. <lang R> as.roman(1666) # MDCLXVI </lang>
Ruby
Roman numeral generation was used as an example for demonstrating Test Driven Development in Ruby. The solution came to be: <lang ruby>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</lang>
Scheme
This uses format directives supported in Chez Scheme since v6.9b; YMMV.
<lang scheme> (define (to-roman n)
(format "~@r" n))
</lang>
Tcl
<lang 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
}</lang>
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. <lang Ursala>#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>+-</lang>
This test program applies the function to each member of a list of numbers. <lang Ursala>#show+
test = roman* <1990,2008,1,2,64,124,1666,10001></lang> output:
MCMXC MMVIII I II LXIV CXXIV MDCLXVI MMMMMMMMMMI
Vedit macro language
<lang vedit> 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 </lang>