Roman Number Representation
Ted Horst
Ted.Horst at ubsw.com
Thu Jun 22 14:10:35 EDT 2000
Ok, here is my take on how to do roman numerals in python. rn and an are
simple, basic routines that are only valid for positive numbers. rn2 and
an2 expand into negative numbers and use a goofy notation that I made up for
large numbers. Their range is limited only by your memory and patience.
------8<------romans.py------------8<--------------------------------
# basic roman numeral conversion by Ted Horst <Ted.Horst at ubsw.com>
# used as part of JulianFormatting
# which is part of Julian, a date, time, timezone package
# comments, fixes, extensions appreciated
'''module to convert between roman and arabic numerals.
rn does a simple conversion from a number to a roman numeral string.
an does a simple conversion from a roman numeral string to an int.
rn2 converts a numer to a roman numeral string
using a goofy, made up notaion for large numbers.
an2 converts from a roman numeral string into a long integer
taking in the goofy notation output from rn2.'''
import string
_join = string.join
del string
_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'))
_couples = {'CM':900, 'CD':400, 'XC':90, 'XL':40, 'IX':9, 'IV':4}
_singles = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1}
def rn(num):
'convert from a number to a roman numeral string'
res = ''
for a in _romans:
res = res + a[1]*int(num/a[0])
num = num%a[0]
return res
def an(roman):
'convert from a roman numeral string to an integer'
place = 0
num = 0
while place < len(roman):
if roman[place:place + 2] in _couples.keys():
num = num + _couples[roman[place:place + 2]]
place = place + 2
else:
num = num + _singles[roman[place]]
place = place + 1
return num
def rn2(num):
'convert from a number to a possibly goofy roman numeral string'
num = long(num)
if num < 0:
res = '-'
num = -num
else:
res = ''
if num < 4000:
res = res + rn(num)
else:
if res:
res = '%s(%s)' % (res, _rnb(num))
else:
res = _rnb(num)
return res
def an2(roman):
'convert from a possibly goofy roman numeral string to a long'
return eval(_subit(roman) or '0')
def _rnb(num):
exp = (len(repr(num)) - 2)/3
if exp == 1:
expr = 'M'
else:
if exp < 4000:
expr = 'M**%s' % rn(exp)
else:
expr = 'M**(%s)' % rn2(exp)
mag = 1000L**exp
co = num/(mag)
if co == 1:
cor = ''
else:
cor = '%s*' % rn(co)
rem = num - co*mag
if rem:
remr = '+%s' % rn2(rem)
else:
remr = ''
return '%s%s%s' % (cor, expr, remr)
def _subit(as):
state = 0
ret = []
tn = ''
for a in as:
if a in 'MDCLXVI':
state = 1
tn = tn + a
else:
if state == 1:
num = long(an(tn))
ret.append(repr(num))
tn = ''
state = 0
ret.append(a)
if state == 1:
num = long(an(tn))
ret.append(repr(num))
return _join(ret, '')
def test():
import traceback
try:
for i in range(4000):
if an(rn(i)) != i:
raise ValueError
for i in range(-3999, 4000):
if an2(rn2(i)) != i:
raise ValueError
for j in range(10):
for i in (10L**j, 10L**j+1, 10L**j-1,
-10L**j, -10L**j+1, -10L**j-1):
if an2(rn2(i)) != i:
raise ValueError
for j in range(4):
for i in (10L**10**j, 10L**10**j+1, 10L**10**j-1,
-10L**10**j, -10L**10**j+1, -10L**10**j-1):
if an2(rn2(i)) != i:
raise ValueError
for i in (long(1e200), 9L**3000):
if an2(rn2(i)) != i:
raise ValueError
except:
print 'FAILED TO PRESERVE', i, 'IN TRANSLATION'
traceback.print_exc()
else:
print 'ALL TESTS PASSED'
-----8<-----end romans.py-----8<-------------------
Ted Horst <Ted.Horst at ubsw.com>
More information about the Python-list
mailing list