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