Conversor de Numerais Romanos bi-direcional em JavaScript e Python.
Código fonte em HTML + JavaScript
<!DOCTYPE html><html><head>
<meta charset="utf-8">
<title>Conversor de Numerais Romanos</title>
<style>
body {
font-family: "Arial", Sans-Serif;
font-size: 12pt;
}
</style>
</head>
<body><h2>Conversor de Numerais Romanos</h2>
<ul>
<li>Numerais romanos representam posicionalmente valores de 1 a 3999</li>
<li>O uso de parênteses para representar milhar não é convencional</li>
<li>O número romano mais longo tem 15 caracteres (3888)</li>
<li>Não existe símbolo para zero</li>
</ul>
<!-- Interface HTML, campos de entrada e saída de dados -->
<table><tr><td>
<label for="roman">Numeral Romano:</label><br>
<label for="arabic">Numeral Arábico:</label></td><td>
<input type="text" id="roman" name="roman" oninput="r2a()"><br>
<input type="number" id="arabic" name="arabic" oninput="a2r()">
</td></tr></table>
<!-- JS funções chamadas ao mudar os dados nos campos acima -->
<script>
function r2a() {
map = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000};
s = get("roman").toUpperCase().replaceAll(' ', ''); // --> maiúsculas, remove espaços
put("arabic", [...s].reduce((r,c,i,s) => map[s[i+1]] > map[c] ? r-map[c] : r+map[c], 0));
}
function a2r() {
s = "M1000CM900D500CD400C100XC90L50XL40X10IX9V5IV4I1";
f=n=>n<4e3?s.replace(/(\D+)(\d+)/g,(_,r,d)=>r.repeat(n/d,n%=d)):`(${f(n/1e3)})`+f(n%1e3);
put("roman", f(get("arabic")));
}
function get(i) { // lê dado do campo i (input)
return document.getElementById(i).value;
}
function put(o,v) { // grava dado no campo o (output)
document.getElementById(o).value=v;
}
</script>
</body></html>
Código fonte em python
romanConvert.py
# roman numerals convertion library by Rogério Neves
c="M CM D CD C XC L XL X IX V IV I".split() # Code/symbol
v=[1000,900,500,400,100,90,50,40,10,9,5,4,1] # Value
def a2r(A,R=''):
''' Convert Arabic to Roman numerals'''
for x,y in zip(c,v):
if n:=A//y:R+=x*n;A%=n*y
return R
def r2a(R):
''' Convert Roman to Arabic numerals'''
A,x,R=0,0,R.upper()
while x<len(R):
if R[x:x+2] in c:
A+=v[c.index(R[x:x+2])]
x+=1
elif R[x:x+1] in c:
A+=v[c.index(R[x:x+1])]
x+=1
return A
def test():
''' Test cases '''
help(r2a)
help(a2r)
print("r2a('MMMCMXCIX')) --> ", r2a('MMMCMXCIX'))
print('a2r(3999) -->', a2r(3999))
print("r2a('CDXLIV') --> ", r2a('CDXLIV'))
print('a2r(444) -->', a2r(444))
print('a2r(3888) -->', a2r(3888))
print("r2a('MMMDCCCLXXXVIII') --> ", r2a('MMMDCCCLXXXVIII'))
for i in range(4000):
if len(a2r(i))>13:
print(f"({len(a2r(i))}) {a2r(i)} = {i}")
if __name__ == "__main__":
test()
Saída (test block)

Uso como biblioteca
import romanConvert as rc
help(rc)
print(f'{rc.r2a('MMMCMXCIX') = }')
print(f'{rc.a2r(494) = }')
from romanConvert import a2r
longRomanNumber = a2r(3888)