ROMAN NUMERAL CONVERSION ROUTINES FOR THE HP48 15Mar92
The following two routines translate between integers and roman numerals.
The Roman system uses 7 letters to represent different weightings:
M 1000
D 500
C 100
L 50
X 10
V 5
I 1
These are assembled additively into a string, largest numbers being left
justified:
e.g. "MCL" is 1150
In addition any character may be preceeded by one of the following:
C 100
X 10
I 1
and this is subtracted from the character. So 9 is "IX" and NOT "VIIII"
However "IM" is not allowed for 999. The preceeding character must be
the next least significant of the ones listed above, e.g. "CMXCIX" for 999.
Only one preceeding character is allowed at a time, 8 is NOT "IIX" it should
be represented by "VIII".
Finally a whole expression can be multiplied by 1000 if a bar covers it.
This aspect is not reproduced in the following programs.
->RN: Takes a number from the stack and returns a string containing the
roman numeral. The result is "tagged" with the original number from
the stack. If the stack is empty then a message indicating the
correct syntax for the routine is returned. If the number has a
fractional part then the fraction is ignored. If the number is greater
than 10000 then only the part of the number <10000 is processed, the
resulting string is preceeded with a "+" to indicate this. This
prevents the creation of very long roman numerals with dozens of
preceeding "M"'s, that take ages to process.
RN->: Takes a string from the stack and returns the equivalent integer.
The result is "tagged" with the original string from the stack.
If the stack is empty then a message indicating the correct syntax
for the routine is returned. If the string contains characters other
than "MDCLXVI" then the program fails. The routine will translate all
legal roman numerals, it will also translate some non legal ones:
e.g. "VX" (5) "MIM" (1999), however it will not translate "IIV"
correctly.
HP48 ASCII CODE for \->RN.....................................................
%%HP: T(3)A(D)F(.);
\<< DEPTH
IF THEN IP "MDCLXVI" "CCXXII " { 1000 500 @ Create four lists that
100 50 10 5 1 } { 100 100 10 10 1 1 0 } @ define the translation
\-> r rs w ws
\<< DUP DUP 10000 >
IF THEN 10000 MOD "+" @ If greater than 10000 then
ELSE "" @ truncate input and add "+"
END
1 r SIZE FOR i @ For each character in the
r i i SUB rs i i SUB w i GET ws i GET@ translation get the weighting
\-> r1 r2 w1 w2 @ from the list
\<<
WHILE OVER w1 \>=
REPEAT SWAP w1 - SWAP r1 + @ subtract weighting as
END @ many times as possible.
IF OVER w2 + w1 \>= @ Check if one more can be
THEN SWAP w1 - w2 + SWAP r2 + r1 + @ subtracted with preceeding
END @ character.
\>>
NEXT
\>> SWAP DROP SWAP \->TAG @ Tag output with input integer
ELSE
"INT \-> Roman Numeral $" @ Print this string as help
DOERR
END
\>>
HP48 ASCII CODE for RN\->.....................................................
%%HP: T(3)A(D)F(.);
\<< DEPTH IF
THEN EVAL DUP SIZE "IVXLCDM" { 1 5 10 @ Create two lists for
50 100 500 1000 } \-> s l r w @ the translation
\<< { }
1 l FOR i @ for each character in the input
s i i SUB r SWAP POS w SWAP GET + @ build a list of the weights
NEXT 0 + l \-> v l
\<< 0 1 l FOR i @ For the list of weights
v i GET DUP v i 1 + GET < @ subtract if followed by a
IF THEN - ELSE + END @ larger number else add
NEXT s \->TAG @ Tag output with input string
\>>
\>>
ELSE
"Roman Numeral $ \-> INT" @ Print this string as help
DOERR
END
\>>