
Decimals to Roman numerals—here we hit all the limitations of Bash shell scripting.
My last few articles have given me a chance to relive my undergraduate computer science degree and code a Roman numeral to decimal converter. It's quite handy when you're watching old movies (when was MCMLVII anyway?), and the basic coding algorithm was reasonably straightforward. (See Dave's "Roman Numerals and Bash" and "More Roman Numerals and Bash".)
The trick with Roman numerals, however, is that it's what's known as a subtractive notation. In other words, it's not a position → value or even symbol → value notation, but a sort of hybrid. MM = 2000, and C = 100, but MMC and MCM are quite different: the former is 2100, and the latter is 1000 + (–100 + 1000) = 1900.
This means that the conversion isn't quite as simple as a mapping table, which makes it a good homework assignment for young comp-sci students!
Let's Write Some Code
In the Roman numeral to decimal conversion, a lot of the key work was done by this simple function:
mapit() {
case $1 in
I|i) value=1 ;;
V|v) value=5 ;;
X|x) value=10 ;;
L|l) value=50 ;;
C|c) value=100 ;;
D|d) value=500 ;;
M|m) value=1000 ;;
* ) echo "Error: Value $1 unknown" >&2 ; exit 2 ;;
esac
}
You'll need this function to proceed, but as a cascading set of conditional statements. Indeed, in its simple form, you could code a decimal to Roman numeral converter like this:
while [ $decvalue -gt 0 ] ; do
if [ $decvalue -gt 1000 ] ; then
romanvalue="$romanvalue M"
decvalue=$(( $decvalue - 1000 ))
elif [ $decvalue -gt 500 ] ; then
romanvalue="$romanvalue D"
decvalue=$(( $decvalue - 500 ))
elif [ $decvalue -gt 100 ] ; then
romanvalue="$romanvalue C"
decvalue=$(( $decvalue - 100 ))
elif [ $decvalue -gt 50 ] ; then
romanvalue="$romanvalue L"
decvalue=$(( $decvalue - 50 ))
elif [ $decvalue -gt 10 ] ; then
romanvalue="$romanvalue X"
decvalue=$(( $decvalue - 10 ))
elif [ $decvalue -gt 5 ] ; then
romanvalue="$romanvalue V"
decvalue=$(( $decvalue - 5 ))
elif [ $decvalue -ge 1 ] ; then
romanvalue="$romanvalue I"
decvalue=$(( $decvalue - 1 ))
fi
done
This actually works, though the results are, um, a bit clunky:
$ sh 2roman.sh 25
converts to roman numeral X X I I I I I
Or, more overwhelming: