Roman numerals/Encode: Difference between revisions
m (→{{header|Retro}}: update for retro-11.0) |
|||
Line 603: | Line 603: | ||
{{works with|D|2}} Simple version for numbers smaller than 5000. |
{{works with|D|2}} Simple version for numbers smaller than 5000. |
||
<lang d> |
<lang d> |
||
int[] weights = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; |
|||
string[] symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", |
|||
"V", "IV", "I"]; |
|||
} |
|||
string toRoman(int n) { |
string toRoman(int n) { |
Revision as of 14:03, 31 March 2011
You are encouraged to solve this task according to the task description, using any language you may know.
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.
ActionScript
<lang ActionScript>function arabic2roman(num:Number):String { var lookup:Object = {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}; var roman:String = "", i:String; for (i in lookup) { while (num >= lookup[i]) { roman += i; num -= lookup[i]; } } return roman; } trace("1990 in roman is " + arabic2roman(1990)); trace("2008 in roman is " + arabic2roman(2008)); trace("1666 in roman is " + arabic2roman(1666)); </lang> Output:
1990 in roman is MCMXC 2008 in roman is MMVIII 1666 in roman is MDCLXVI
And the reverse: <lang ActionScript>function roman2arabic(roman:String):Number { var romanArr:Array = roman.toUpperCase().split(); var lookup:Object = {I:1, V:5, X:10, L:50, C:100, D:500, M:1000}; var num:Number = 0, val:Number = 0; while (romanArr.length) { val = lookup[romanArr.shift()]; num += val * (val < lookup[romanArr[0]] ? -1 : 1); } return num; } trace("MCMXC in arabic is " + roman2arabic("MCMXC")); trace("MMVIII in arabic is " + roman2arabic("MMVIII")); trace("MDCLXVI in arabic is " + roman2arabic("MDCLXVI"));</lang> Output:
MCMXC in arabic is 1990 MMVIII in arabic is 2008 MDCLXVI in arabic is 1666
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 algol68>[]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 (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
ALGOL W
<lang algolw>BEGIN
PROCEDURE ROMAN (INTEGER VALUE NUMBER; STRING(15) RESULT CHARACTERS; INTEGER RESULT LENGTH);
COMMENT Returns the Roman number of an integer between 1 and 3999. "MMMDCCCLXXXVIII" (15 characters long) is the longest Roman number under 4000; BEGIN INTEGER PLACE, POWER;
PROCEDURE APPEND (STRING(1) VALUE C); BEGIN CHARACTERS(LENGTH|1) := C; LENGTH := LENGTH + 1 END;
PROCEDURE I; APPEND(CASE PLACE OF ("I","X","C","M")); PROCEDURE V; APPEND(CASE PLACE OF ("V","L","D")); PROCEDURE X; APPEND(CASE PLACE OF ("X","C","M"));
ASSERT (NUMBER >= 1) AND (NUMBER < 4000);
CHARACTERS := " "; LENGTH := 0; POWER := 1000; PLACE := 4; WHILE PLACE > 0 DO BEGIN CASE NUMBER DIV POWER + 1 OF BEGIN BEGIN END; BEGIN I END; BEGIN I; I END; BEGIN I; I; I END; BEGIN I; V END; BEGIN V END; BEGIN V; I END; BEGIN V; I; I END; BEGIN V; I; I; I END; BEGIN I; X END END; NUMBER := NUMBER REM POWER; POWER := POWER DIV 10; PLACE := PLACE - 1 END END ROMAN;
INTEGER I; STRING(15) S;
ROMAN(1, S, I); WRITE(S, I); ROMAN(3999, S, I); WRITE(S, I); ROMAN(3888, S, I); WRITE(S, I); ROMAN(2009, S, I); WRITE(S, I); ROMAN(405, S, I); WRITE(S, I); END.</lang> Output:
I 1 MMMCMXCIX 9 MMMDCCCLXXXVIII 15 MMIX 4 CDV 3
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
ZX Spectrum Basic
<lang zxbasic> 10 DATA 1000,"M",900,"CM"
20 DATA 500,"D",400,"CD" 30 DATA 100,"C",90,"XC" 40 DATA 50,"L",40,"XL" 50 DATA 10,"X",9,"IX" 60 DATA 5,"V",4,"IV",1,"I" 70 INPUT "Enter an arabic number: ";V 80 LET VALUE=V 90 LET V$=""
100 FOR I=0 TO 12 110 READ A,R$ 120 IF V<A THEN GO TO 160 130 LET V$=V$+R$ 140 LET V=V-A 150 GO TO 120 160 NEXT I 170 PRINT VALUE;"=";V$</lang>
C
<lang c>#include <stdlib.h>
- include <stdio.h>
/*
* 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. */
void roman(char *s, unsigned int n) {
if (n == 0) { fputs(stderr, "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]; unsigned int i; for (i = 1 ; i < 4000 ; ++i) { roman(buffer, i); printf("%4u: %s\n", i, buffer); } return EXIT_SUCCESS;
}</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 csharp>using System; class Program {
static uint[] 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 string ToRoman(uint number) { string value = ""; for (int i = 0; i < nums.Length && number != 0; i++) { while (number >= nums[i]) { number -= nums[i]; value += rum[i]; } } return value; }
static void Main() { for (uint number = 1; number <= 1 << 10; number *= 2) { Console.WriteLine("{0} = {1}", number, ToRoman(number)); } }
}</lang>
Output:
1 = I 2 = II 4 = IV 8 = VIII 16 = XVI 32 = XXXII 64 = LXIV 128 = CXXVIII 256 = CCLVI 512 = DXII 1024 = MXXIV
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, NULL }; // 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
<lang lisp>(defun roman-numeral (n)
(format nil "~@R" n))</lang>
Clojure
<lang 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"]
</lang>
D
Simple version for numbers smaller than 5000.
<lang d> int[] weights = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; string[] symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX",
"V", "IV", "I"];
string toRoman(int n) {
auto app = appender!string; foreach (i, w; weights) { while (n >= w) { app.put(symbols[i]); n -= w; } if (n == 0) break; } return app.data;
}
int toArabic(string s) {
int arabic; foreach (m; match(s, "CM|CD|XC|XL|IX|IV|[MDCLXVI]")) { arabic += weights[symbols.indexOf(m.hit)]; } return arabic;
}</lang>
<lang d>unittest {
assert(toRoman(455) == "CDLV"); assert(toRoman(3456) == "MMMCDLVI"); assert(toRoman(2488) == "MMCDLXXXVIII");
assert(toArabic("CDLV") == 455); assert(toArabic("MMMCDLVI") == 3456); assert(toArabic("MMCDLXXXVIII") == 2488);
}</lang>
Erlang
<lang erlang>-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].</lang>
sample:
1> c(roman). {ok,roman} 2> roman:to_roman(1999). "MCMXCIX" 3> roman:to_roman(25). "XXV" 4> roman:to_roman(944). "CMXLIV"
Euphoria
<lang Euphoria>constant arabic = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 } constant roman = {"M", "CM", "D","CD", "C","XC","L","XL","X","IX","V","IV","I"}
function toRoman(integer val)
sequence result result = "" for i = 1 to 13 do while val >= arabic[i] do result &= roman[i] val -= arabic[i] end while end for return result
end function
printf(1,"%d = %s\n",{2009,toRoman(2009)}) printf(1,"%d = %s\n",{1666,toRoman(1666)}) printf(1,"%d = %s\n",{3888,toRoman(3888)})</lang>
Output:
2009 = MMIX 1666 = MDCLXVI 3888 = MMMDCCCLXXXVIII
Factor
A roman numeral library ships with Factor. <lang factor>USE: roman ( scratchpad ) 3333 >roman . "mmmcccxxxiii"</lang>
Parts of the implementation:
<lang factor>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 ;</lang>
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
<lang forth>: vector create ( n -- ) 0 do , loop does> ( n -- ) swap cells + @ execute ; \ these are ( numerals -- numerals )
- ,I dup c@ C, ; : ,V dup 1 + c@ C, ; : ,X dup 2 + c@ C, ;
\ 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 -- c-addr u )
dup 0 4000 within 0= abort" EX LIMITO!" HERE SWAP s" IVXLCDM" drop swap roman-rec HERE OVER - ;
1999 roman type \ MCMXCIX
25 roman type \ XXV 944 roman type \ CMXLIV</lang>
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
Go
If you see boxes in the code below, those are supposed to be the Unicode combining overline (U+0305) and look like IVXLCDM. Or, if you see overstruck combinations of letters, that's a different font rendering problem. (If you need roman numerals > 3999 reliably, it might best to stick to chiseling them in stone...) <lang go>package main
import "fmt"
var (
m0 = []string{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"} m1 = []string{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"} m2 = []string{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"} m3 = []string{"", "M", "MM", "MMM", "I̅V̅", "V̅", "V̅I̅", "V̅I̅I̅", "V̅I̅I̅I̅", "I̅X̅"} m4 = []string{"", "X̅", "X̅X̅", "X̅X̅X̅", "X̅L̅", "L̅", "L̅X̅", "L̅X̅X̅", "L̅X̅X̅X̅", "X̅C̅"} m5 = []string{"", "C̅", "C̅C̅", "C̅C̅C̅", "C̅D̅", "D̅", "D̅C̅", "D̅C̅C̅", "D̅C̅C̅C̅", "C̅M̅"} m6 = []string{"", "M̅", "M̅M̅", "M̅M̅M̅"}
)
func formatRoman(n int) (string, bool) {
if n < 1 || n >= 4e6 { return "", false } // this is efficient in Go. the seven operands are evaluated, // then a single allocation is made of the exact size needed for the result. return m6[n/1e6] + m5[n%1e6/1e5] + m4[n%1e5/1e4] + m3[n%1e4/1e3] + m2[n%1e3/1e2] + m1[n%100/10] + m0[n%10], true
}
func main() {
// WP test cases for _, n := range []int{-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 25, 30, 35, 40, 45, 49, 50, 60, 69, 70, 76, 80, 90, 99, 100, 110, 150, 200, 300, 400, 499, 500, 600, 666, 700, 800, 900, 999, 1000, 1444, 1666, 1990, 1999, 2000, 2001, 2010, 2012, 2500, 3000, 3888, 3999, 4000, 5000, 6666, 10000, 50000, 100000, 500000, 1000000, 1444000, 1666000, 2000000, 3888888, 3999999, 4000000} { r, ok := formatRoman(n) if ok { fmt.Println(n, r) } else { fmt.Println(n, "not representable") } }
}</lang>
Groovy
<lang groovy>symbols = [ 1:'I', 4:'IV', 5:'V', 9:'IX', 10:'X', 40:'XL', 50:'L', 90:'XC', 100:'C', 400:'CD', 500:'D', 900:'CM', 1000:'M' ]
def roman(arabic) {
def result = "" symbols.keySet().sort().reverse().each { while (arabic >= it) { arabic-=it result+=symbols[it] } } return result
} assert roman(1) == 'I' assert roman(2) == 'II' assert roman(4) == 'IV' assert roman(8) == 'VIII' assert roman(16) == 'XVI' assert roman(32) == 'XXXII' assert roman(25) == 'XXV' assert roman(64) == 'LXIV' assert roman(128) == 'CXXVIII' assert roman(256) == 'CCLVI' assert roman(512) == 'DXII' assert roman(954) == 'CMLIV' assert roman(1024) == 'MXXIV' assert roman(1666) == 'MDCLXVI' assert roman(1990) == 'MCMXC' assert roman(2008) == 'MMVIII'</lang>
Haskell
With an explicit decimal digit representation list:
<lang haskell>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</lang>
Output:
<lang haskell>*Main> map toRoman [1999,25,944] ["MCMXCIX","XXV","CMXLIV"]</lang>
HicEst
<lang hicest>CHARACTER Roman*20
CALL RomanNumeral(1990, Roman) ! MCMXC CALL RomanNumeral(2008, Roman) ! MMVIII CALL RomanNumeral(1666, Roman) ! MDCLXVI
END
SUBROUTINE RomanNumeral( arabic, roman)
CHARACTER roman DIMENSION ddec(13) DATA ddec/1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1/
roman = ' ' todo = arabic DO d = 1, 13 DO rep = 1, todo / ddec(d) roman = TRIM(roman) // TRIM(CHAR(d, 13, "M CM D CD C XC L XL X OX V IV I ")) todo = todo - ddec(d) ENDDO ENDDO
END</lang>
Icon and Unicon
<lang Icon>link numbers # commas, roman
procedure main(arglist) every x := !arglist do
write(commas(x), " -> ",roman(x)|"*** can't convert to Roman numerals ***")
end</lang>
fib provides numbers:roman as seen below and based on a James Gimple SNOBOL4 function.
<lang Icon>procedure roman(n) #: convert integer to Roman numeral
local arabic, result static equiv
initial equiv := ["","I","II","III","IV","V","VI","VII","VIII","IX"]
integer(n) > 0 | fail result := "" every arabic := !n do result := map(result,"IVXLCDM","XLCDM**") || equiv[arabic + 1] if find("*",result) then fail else return result
end</lang>
Sample output:
#roman.exe 3 4 8 49 2010 1666 3000 3999 4000 3 -> III 4 -> IV 8 -> VIII 49 -> XLIX 2,010 -> MMX 1,666 -> MDCLXVI 3,999 -> MMMCMXCIX 4,000 -> *** can't convert to Roman numerals ***
Io
<lang Io>Roman := Object clone do (
nums := list(1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1) rum := list("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I") numeral := method(number, result := "" for(i, 0, nums size, if(number == 0, break) while(number >= nums at(i), number = number - nums at(i) result = result .. rum at(i) ) ) return result )
)
Roman numeral(1666) println</lang>
J
rfd obtains Roman numerals from decimals.
<lang j>R1000=. ;L:1 ,{ <@(<;._1);._2]0 :0
C CC CCC CD D DC DCC DCCC CM X XX XXX XL L LX LXX LXXX XC I II III IV V VI VII VIII IX
)
rfd=: ('M' $~ <.@%&1000) , R1000 {::~ 1000&|</lang>
For example:<lang j> rfd 1234 MCCXXXIV
rfd 567
DLXVII
rfd 89
LXXXIX</lang>
Derived from the J Wiki. Further examples of use will be found there.
Java
The conversion function throws an IllegalArgumentException for non-positive numbers, since Java does not have unsigned primitives. <lang java>public class RN {
enum Numeral { 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); int weigth;
Numeral(int weigth) { this.weigth = weigth; } };
public static String roman(long n) { if( n <= 0) { throw new IllegalArgumentException(); } StringBuilder buf = new StringBuilder();
final Numeral[] values = Numeral.values(); for (int i = values.length - 1; i >= 0; i--) { while (n >= values[i].weigth) { buf.append(values[i]); n -= values[i].weigth; } } return buf.toString(); }
public static void test(long n) { System.out.println(n + " = " + roman(n)); }
public static void main(String[] args) { test(1999); test(25); test(944); test(0); }
}</lang> Output:
1999 = MCMXCIX 25 = XXV 944 = CMXLIV Exception in thread "main" java.lang.IllegalArgumentException at RN.roman(RN.java:15) at RN.test(RN.java:31) at RN.main(RN.java:38)
JavaScript
<lang javascript>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"</lang>
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
<lang 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</lang>
<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</lang>
LotusScript
<lang lss> Function toRoman(value) As String Dim arabic(12) As Integer Dim roman(12) As String
arabic(0) = 1000 arabic(1) = 900 arabic(2) = 500 arabic(3) = 400 arabic(4) = 100 arabic(5) = 90 arabic(6) = 50 arabic(7) = 40 arabic(8) = 10 arabic(9) = 9 arabic(10) = 5 arabic(11) = 4 arabic(12) = 1
roman(0) = "M" roman(1) = "CM" roman(2) = "D" roman(3) = "CD" roman(4) = "C" roman(5) = "XC" roman(6) = "L" roman(7) = "XL" roman(8) = "X" roman(9) = "IX" roman(10) = "V" roman(11) = "IV" roman(12) = "I"
Dim i As Integer, 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
</lang>
Lua
<lang 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()</lang>
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>
MUMPS
<lang MUMPS>TOROMAN(INPUT)
;Converts INPUT into a Roman numeral. INPUT must be an integer between 1 and 3999 ;OUTPUT is the string to return ;I is a loop variable ;CURRVAL is the current value in the loop QUIT:($FIND(INPUT,".")>1)!(INPUT<=0)!(INPUT>3999) "Invalid input" NEW OUTPUT,I,CURRVAL SET OUTPUT="",CURRVAL=INPUT SET:$DATA(ROMANNUM)=0 ROMANNUM="I^IV^V^IX^X^XL^L^XC^C^CD^D^CM^M" SET:$DATA(ROMANVAL)=0 ROMANVAL="1^4^5^9^10^40^50^90^100^400^500^900^1000" FOR I=$LENGTH(ROMANVAL,"^"):-1:1 DO .FOR Q:CURRVAL<$PIECE(ROMANVAL,"^",I) SET OUTPUT=OUTPUT_$PIECE(ROMANNUM,"^",I),CURRVAL=CURRVAL-$PIECE(ROMANVAL,"^",I) KILL I,CURRVAL QUIT OUTPUT</lang>
Output:
USER>W $$ROMAN^ROSETTA(1666) MDCLXVI USER>W $$TOROMAN^ROSETTA(2010) MMX USER>W $$TOROMAN^ROSETTA(949) CMXLIX USER>W $$TOROMAN^ROSETTA(949.24) Invalid input USER>W $$TOROMAN^ROSETTA(-949) Invalid input
Objeck
<lang objeck> bundle Default {
class Roman { nums: static : Int[]; rum : static : String[]; function : Init() ~ Nil { nums := [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; rum := ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]; }
function : native : ToRoman(number : Int) ~ String { result := "";
for(i :=0; i < nums->Size(); i += 1;) { while(number >= nums[i]) { result->Append(rum[i]); number -= nums[i]; }; };
return result; }
function : Main(args : String[]) ~ Nil { Init();
ToRoman(1999)->PrintLine(); ToRoman(25)->PrintLine(); ToRoman(944)->PrintLine(); } }
} </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']
Oz
<lang oz>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}</lang>
PARI/GP
Old-style Roman numerals <lang>oldRoman(n)={
while(n>999999, n-=1000000; print1("((((I))))") ); if(n>499999, n-=500000; print1("I))))") ); while(n>99999, n-=100000; print1("(((I)))") ); if(n>49999, n-=50000; print1("I)))") ); while(n>9999, n-=10000; print1("((I))") ); if(n>4999, n-=5000; print1("I))") ); while(n>999, n-=1000; print1("(I)") ); if(n>499, n-=500; print1("I)") ); while(n>99, n-=100; print1("C") ); if(n>49, n-=50; print1("L"); ); while(n>9, n-=10; print1("X") ); if(n>4, n-=5; print1("V"); ); while(n, n--; print1("I") ); print()
};</lang>
This simple version of medieval Roman numerals does not handle large numbers. <lang>medievalRoman(n)={
while(n>999, n-=1000; print1("M") ); if(n>899, n-=900; print1("CM") ); if(n>499, n-=500; print1("D") ); if(n>399, n-=400; print1("CD") ); while(n>99, n-=100; print1("C") ); if(n>89, n-=90; print1("XC") ); if(n>49, n-=50; print1("L") ); if(n>39, n-=40; print1("XL") ); while(n>9, n-=10; print1("X") ); if(n>8, n-=9; print1("IX") ); if(n>4, n-=5; print1("V") ); if(n>3, n-=4; print1("IV") ); while(n, n--; print1("I") ); print()
};</lang>
Perl
Perligata outputs numbers in Arabic, but the verb come ("beautify") may be used to convert numbers to proper Roman numerals:
<lang perl>per quisque in I tum C conscribementum sic
hoc tum duos multiplicamentum comementum egresso scribe.
cis</lang>
Perl 6
<lang perl6>my %symbols =
1 => "I", 5 => "V", 10 => "X", 50 => "L", 100 => "C", 500 => "D", 1_000 => "M";
my @subtractors =
1_000, 100, 500, 100, 100, 10, 50, 10, 10, 1, 5, 1, 1, 0;
sub roman (Int $n where { $n > 0 }) {
return %symbols{$n} if %symbols{$n}; for @subtractors -> $cut, $minus { $cut < $n and return %symbols{$cut} ~ roman($n - $cut); $cut - $minus <= $n and return %symbols{$minus} ~ roman($n + $minus); }
}</lang>
Sample usage
<lang perl6>for 1 .. 2_010 -> $x {
say roman($x);
}</lang>
PHP
<lang php> /**
* 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; } </lang>
PicoLisp
<lang 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) ) ) ) )</lang>
Output:
: (roman 1009) -> "MIX" : (roman 1666) -> "MDCLXVI"
Pike
<lang pike>import String; int main(){
write(int2roman(2009) + "\n"); write(int2roman(1666) + "\n"); write(int2roman(1337) + "\n");
}</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>
PL/I
<lang 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; </lang>
PowerBASIC
<lang powerbasic>FUNCTION toRoman(value AS INTEGER) AS STRING
DIM arabic(0 TO 12) AS INTEGER DIM roman(0 TO 12) AS STRING ARRAY ASSIGN arabic() = 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ARRAY ASSIGN roman() = "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"
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
FUNCTION PBMAIN
'Testing ? "2009 = " & toRoman(2009) ? "1666 = " & toRoman(1666) ? "3888 = " & toRoman(3888)
END FUNCTION</lang>
Prolog
Works with SWI-Prolog and library clpfd.
Library clpfd assures that the program works in both managements : Roman towards Arabic and Arabic towards Roman.
<lang Prolog>roman :-
LA = [ _ , 2010, _, 1449, _],
LR = ['MDCCLXXXIX', _ , 'CX', _, 'MDCLXVI'],
maplist(roman, LA, LR),
maplist(my_print,LA, LR).
roman(A, R) :-
A #> 0,
roman(A, [u, t, h, th], LR, []),
label([A]),
parse_Roman(CR, LR, []),
atom_chars(R, CR).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % using DCG
roman(0, []) --> [].
roman(N, [H | T]) --> {N1 #= N / 10, N2 #= N mod 10}, roman(N1, T), unity(N2, H).
unity(1, u) --> ['I']. unity(1, t) --> ['X']. unity(1, h) --> ['C']. unity(1, th)--> ['M'].
unity(4, u) --> ['IV']. unity(4, t) --> ['XL']. unity(4, h) --> ['CD']. unity(4, th)--> ['MMMM'].
unity(5, u) --> ['V']. unity(5, t) --> ['L']. unity(5, h) --> ['D']. unity(5, th)--> ['MMMMM'].
unity(9, u) --> ['IX']. unity(9, t) --> ['XC']. unity(9, h) --> ['CM']. unity(9, th)--> ['MMMMMMMMM'].
unity(0, _) --> [].
unity(V, U)-->
{V #> 5,
V1 #= V - 5},
unity(5, U),
unity(V1, U).
unity(V, U) --> {V #> 1, V #< 4, V1 #= V-1}, unity(1, U), unity(V1, U).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Extraction of roman "lexeme" parse_Roman(['C','M'|T]) --> ['CM'], parse_Roman(T).
parse_Roman(['C','D'|T]) --> ['CD'], parse_Roman(T).
parse_Roman(['X','C'| T]) --> ['XC'], parse_Roman(T).
parse_Roman(['X','L'| T]) -->
['XL'],
parse_Roman(T).
parse_Roman(['I','X'| T]) -->
['IX'],
parse_Roman(T).
parse_Roman(['I','V'| T]) -->
['IV'],
parse_Roman(T).
parse_Roman([H | T]) --> [H], parse_Roman(T).
parse_Roman([]) -->
[].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% my_print(A, R) :- format('~w in roman is ~w~n', [A, R]). </lang> Output :
?- roman. 1789 in roman is MDCCLXXXIX 2010 in roman is MMX 110 in roman is CX 1449 in roman is MCDXLIX 1666 in roman is MDCLXVI true .
Protium
Roman numbers are built in to Protium as a particular form of national number. However, for the sake of the task the _RO opcode has been defined. <lang html><@ DEFUDOLITLIT>_RO|__Transformer|<@ DEFKEYPAR>__NationalNumericID|2</@><@ LETRESCS%NNMPAR>...|1</@></@>
<@ ENU$$DLSTLITLIT>1990,2008,1,2,64,124,1666,10001|,| <@ SAYELTLST>...</@> is <@ SAY_ROELTLSTLIT>...|RomanLowerUnicode</@> <@ SAY_ROELTLSTLIT>...|RomanUpperUnicode</@> <@ SAY_ROELTLSTLIT>...|RomanASCII</@> </@></lang>
Same code in padded-out, variable-length English dialect <lang html><# DEFINE USERDEFINEDOPCODE LITERAL LITERAL>_RO|__Transformer|<# DEFINE KEYWORD PARAMETER>__NationalNumericID|2</#><# LET RESULT CAST NATIONALNUMBER PARAMETER>...|1</#></#>
<# ENUMERATION LAMBDASPECIFIEDDELMITER LIST LITERAL LITERAL>1990,2008,1,2,64,124,1666,10001|,| <# SAY ELEMENT LIST>...</#> is <# SAY _RO ELEMENT LIST LITERAL>...|RomanLowerUnicode</#> <# SAY _RO ELEMENT LIST LITERAL>...|RomanUpperUnicode</#> <# SAY _RO ELEMENT LIST LITERAL>...|RomanASCII</#> </#></lang>
Output. Notice here the three different ways of representing the results. For reasons for notational differences, see wp:Roman_numerals#Alternate_forms
1990 is ⅿⅽⅿⅹⅽ ⅯⅭⅯⅩⅭ MCMXC 2008 is ⅿⅿⅷ ⅯⅯⅧ MMVIII 1 is ⅰ Ⅰ I 2 is ⅱ Ⅱ II 64 is ⅼⅹⅳ ⅬⅩⅣ LXIV 124 is ⅽⅹⅹⅳ ⅭⅩⅩⅣ CXXIV 1666 is ⅿⅾⅽⅼⅹⅵ ⅯⅮⅭⅬⅩⅥ MDCLXVI 10001 is ⅿⅿⅿⅿⅿⅿⅿⅿⅿⅿⅰ ↂⅠ MMMMMMMMMMI
PureBasic
<lang 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</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>
It is more Pythonic to use zip to iterate over two lists together: <lang python>anums = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1] rnums = "M CM D CD C XC L XL X IX V IV I".split()
def to_roman(x):
ret = [] for a,r in zip(anums, rnums): n,x = divmod(x,a) ret.append(r*n) return .join(ret)
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,2010,2011,2500, 3000,3999) for val in test: print '%d - %s'%(val, to_roman(val))
</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>
Retro
This is a port of the Forth code; but returns a string rather than displaying the roman numerals. It only handles numbers between 1 and 3999.
<lang Retro>
- vector ( ...n"- )
here [ &, times ] dip : .data ` swap ` + ` @ ` do ` ; ;
- .I dup @ ^buffer'add ;
- .V dup 1 + @ ^buffer'add ;
- .X dup 2 + @ ^buffer'add ;
[ .I .X drop ] [ .V .I .I .I drop ] [ .V .I .I drop ] [ .V .I drop ] [ .V drop ] [ .I .V drop ] [ .I .I .I drop ] [ .I .I drop ] [ .I drop ] &drop 10 vector .digit
- record ( an- )
10 /mod dup [ [ over 2 + ] dip record ] &drop if .digit ;
- toRoman ( n-a )
here ^buffer'set dup 1 3999 within 0 = [ "EX LIMITO!\n" ] [ "IVXLCDM" swap record here ] if ;
</lang>
REXX
<lang 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</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>
Scala
<lang scala>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 => ""
}</lang>
Sample:
scala> List(1990, 2008, 1666) map toRoman res55: List[String] = List(MCMXC, MMVIII, MDCLXVI)
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>
SNOBOL4
Adapted from Catspaw SNOBOL Tutorial, Chapter 6
<lang snobol4>
- 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</lang> Outputs:
1999 = MCMXCIX 24 = XXIV 944 = CMXLIV
Here's a non-recursive version, and a Roman-to-Arabic converter to boot.
<lang SNOBOL4>* # Arabic to Roman
define('roman(n)s,ch,val,str') :(roman_end)
roman roman = ge(n,4000) n :s(return)
s = 'M1000 CM900 D500 CD400 C100 XC90 L50 XL40 X10 IX9 V5 IV4 I1 '
rom1 s span(&ucase) . ch break(' ') . val span(' ') = :f(rom2)
str = str dupl(ch,(n / val)) n = remdr(n,val) :(rom1)
rom2 roman = str :(return) roman_end
- # 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 = '2010 1999 1492 1066 476 '
tloop tstr break(' ') . year span(' ') = :f(out)
r = roman(year) rstr = rstr year '=' r ' ' astr = astr r '=' arabic(r) ' ' :(tloop)
out output = rstr; output = astr end</lang>
Output:
2010=MMX 1999=MCMXCIX 1492=MCDXCII 1066=MLXVI 476=CDLXXVI MMX=2010 MCMXCIX=1999 MCDXCII=1492 MLXVI=1066 CDLXXVI=476
TUSCRIPT
<lang tuscript> $$ MODE TUSCRIPT SET numbers="1990'2008'1666",romannumbers="" COMPILE LOOP n=numbers
SET romannumber = ENCODE (n,ROMAN) PRINT "Roman number of ",n, " is ", romannumber SET romannumbers=APPEND (romannumbers,romannumber)
ENDLOOP </lang> Output:
Roman number of 1990 is MCMXC Roman number of 2008 is MMVIII Roman number of 1666 is MDCLXVI
... and back again ... Roman to Arabic numberals <lang tuscript> LOOP r=romannumbers
SET numeral=DECODE (r,ROMAN) PRINT "Roman number ",r," equals ",numeral
ENDLOOP ENDCOMPILE </lang> Output:
Roman number MCMXC equals 1990 Roman number MMVIII equals 2008 Roman number MDCLXVI equals 1666
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>
Visual Basic
<lang vb>Function toRoman(value) As String
Dim arabic As Variant Dim roman As Variant
arabic = Array(1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1) roman = Array("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I")
Dim i As Integer, 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
Sub Main()
MsgBox toRoman(Val(InputBox("Number, please")))
End Sub</lang>
XSLT
<lang xslt> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/data/number"> <xsl:call-template name="for"> <xsl:with-param name="stop">13</xsl:with-param> <xsl:with-param name="value"><xsl:value-of select="@value"></xsl:value-of></xsl:with-param> </xsl:call-template> </xsl:template> <xsl:template name="for"> <xsl:param name="start">1</xsl:param> <xsl:param name="stop">1</xsl:param> <xsl:param name="step">1</xsl:param> <xsl:param name="value">1</xsl:param> <xsl:text/> <xsl:choose> <xsl:when test="($value > /data/roman
/numeral[@pos=$start]/@value or $value = /data/roman /numeral[@pos=$start]/@value) ">
<xsl:value-of select="/data/roman
/numeral[@pos=$start]/@letter"/>
<xsl:call-template name="for"> <xsl:with-param name="stop"> <xsl:value-of select="$stop"/> </xsl:with-param> <xsl:with-param name="start"> <xsl:value-of select="$start"/> </xsl:with-param> <xsl:with-param name="value"> <xsl:value-of select="$value - /data/roman/numeral[@pos=$start]/@value"/> </xsl:with-param> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:if test="$start < $stop"> <xsl:call-template name="for"> <xsl:with-param name="stop"> <xsl:value-of select="$stop"/> </xsl:with-param> <xsl:with-param name="start"> <xsl:value-of select="$start + $step"/> </xsl:with-param> <xsl:with-param name="value"> <xsl:value-of select="$value"/> </xsl:with-param> </xsl:call-template> </xsl:if> </xsl:otherwise> </xsl:choose> </xsl:template>
</xsl:stylesheet> </lang>
- Programming Tasks
- Solutions by Programming Task
- ActionScript
- Ada
- ALGOL 68
- ALGOL W
- AutoHotkey
- AWK
- Examples needing attention
- BASIC
- ZX Spectrum Basic
- C
- C sharp
- C++
- Common Lisp
- Clojure
- D
- Erlang
- Euphoria
- Factor
- FALSE
- Fan
- Forth
- Fortran
- Go
- Groovy
- Haskell
- HicEst
- Icon
- Unicon
- Icon Programming Library
- Io
- J
- Java
- JavaScript
- LaTeX
- Logo
- LotusScript
- Lua
- M4
- Mathematica
- MUMPS
- Objeck
- OCaml
- Oz
- PARI/GP
- Perl
- Perl 6
- PHP
- PicoLisp
- Pike
- PlainTeX
- PL/I
- PowerBASIC
- Prolog
- Protium
- PureBasic
- Python
- R
- Retro
- REXX
- Ruby
- Scala
- Scheme
- Tcl
- SNOBOL4
- TUSCRIPT
- Ursala
- Vedit macro language
- Visual Basic
- XSLT