reptile7's JavaScript blog
Wednesday, September 10, 2014
 
When in Rome
Blog Entry #334

Over and above the New Array Text Pages scripts, Jenson's Easy to Use JavaScripts directory features a Roman Numeral (RomanNumerals.html) Script that I thought we would look at in today's post. The RomanNumerals.html script
(1) converts Arabic numerals to Roman numerals and also
(2) creates a date string whose day and year numbers are expressed in Roman numerals.

On the whole the RomanNumerals.html script is well-designed but it does have one serious flaw, namely, it's not interactive: a corrective demo will be provided at the end of the post.

Numeric inputs

Specifically, the RomanNumerals.html script converts Arabic integers in the range 1-3999 inclusive to their Roman numeral equivalents.

• For a 0 input the script outputs an empty string.

• Floating-point inputs are Math.floor( )ed, e.g., 3.14159 gives III.

• Oddly, the script accommodates negative integer inputs in the range -1 to -3999 inclusive - -25 gives -XXV, e.g. - even though Roman numerals are not really meant for negative numbers.

• For integers larger than 3999 (or smaller than -3999) the script outputs an "Out of Range" string.

Wikipedia's "Roman numerals" entry includes a Special values section that addresses the Roman approaches to zero, fractions, and large numbers. Meanwhile, wikiHow reports that the Romans themselves just wrote MMMM for 4000.

The cast of characters

In his script commentary, Jenson correctly notes:
Roman numerals are based on only two basic digits: 1 and 5.
However, they use different characters depending on the power of 10 being represented.
'1' Roman Numerals
Roman NumeralArabic EquivalentPower of 10
I11 × 100
X101 × 101
C1001 × 102
M10001 × 103
  
'5' Roman Numerals
Roman NumeralArabic EquivalentPower of 10
V55 × 100
L505 × 101
D5005 × 102


The I, V, X, L, C, D, and M Roman numerals are arrayed in ascending order via a romanDigits Object object

romanDigits[0] = "I";
romanDigits[1] = "V";
romanDigits[2] = "X";
romanDigits[3] = "L";
romanDigits[4] = "C";
romanDigits[5] = "D";
romanDigits[6] = "M";
romanDigits.length = 7;


that instantiates the buildArray( ) constructor.

function buildArray( ) { var a = arguments; for (i = 0; i < a.length; i++) this[i] = a[i]; this.length = a.length; }

var romanDigits = new buildArray("I", "V", "X", "L", "C", "D", "M");


Could romanDigits be formulated as a bona fide Array object? That it could.

var romanDigits = ["I", "V", "X", "L", "C", "D", "M"];

Fragment it

For this and the following sections we will work with a 374CCCLXXIV example.

The business end of the RomanNumerals.html script comprises two functions:
(1) an arabicToRoman( ) function effectively divides an inputted Arabic integer into its expanded notation terms;
(2) an arabicToRomanDigit( ) function 'Romanizes' those terms.

A document.write( ) command calls the arabicToRoman( ) function; the 374 argument is given a number identifier.

function arabicToRoman(number) { ... }

document.write("374 in Roman Numerals is ", arabicToRoman(374));


The arabicToRoman( ) body initially declares j, string, and sign variables.

var j, string = "", sign = "";

If number is negative, then sign is set to a stringified minus sign and number is converted to its additive inverse.

if (number < 0) { sign = "-"; number = -number; }

Subsequently number is floor( )ed whether it needs it or not.

number = Math.floor(number);

If number is 4000 or greater, then Out of Range is returned to the calling document.write( ) command and the function exits.

if (number >= 4000) return "Out of Range";

With these preliminaries out of the way, we're ready to get to work on our number=374. A for loop splits 374 into its constituent digits and passes
(a) those digits and
(b) the powers of 10 associated with them
to the arabicToRomanDigit( ) function.

for (j = 0; number > 0; j++) { digit = number % 10; number = (number - digit) / 10; string = arabicToRomanDigit(digit, j) + string; }

The loop runs for three iterations; more generally, it runs as many times as there are digits in number. Noting that

374 = 3 × 102 + 7 × 101 + 4 × 100

here's a rundown of the number processing in those iterations:

j = 0
374 is moduloed by 10 to give 4, which is assigned to a digit variable.
(374 - 4) / 10 gives 37, which is assigned to number.
4 and 0 are passed to the arabicToRomanDigit( ) function.

j = 1
37 is moduloed by 10 to give 7, which is assigned to digit.
(37 - 7) / 10 gives 3, which is assigned to number.
7 and 1 are passed to arabicToRomanDigit( ).

j = 2
3 is moduloed by 10 to give 3, which is assigned to digit.
(3 - 3) / 10 gives 0, which is assigned to number.
3 and 2 are passed to arabicToRomanDigit( ).

With number now 0, the loop stops at the beginning of (what would be) its fourth iteration because the number > 0 condition returns false.

This is not the only way to split number: alternatively, we could stringify number and then go after its characters via, say, the substring( ) method. But given that number is in fact a number (and that we'll be doing some simple arithmetic on the 7 in the arabicToRomanDigit( ) function), the modulo/division approach is the more appropriate way to go.

Romanize it

The arabicToRomanDigit( ) function's digit and j arguments are given the identifiers digit and power, respectively. The arabicToRomanDigit( ) body initially declares i and string variables.

function arabicToRomanDigit(digit, power) { var i, string = ""; ... }

The declarations are followed by a series of if statements that converts Arabic digits to corresponding Roman strings. Following modern practice, 4s and 9s are handled subtractively, e.g., 4 becomes IV and not IIII. A set of romanDigits[n + power * 2] expressions relates the power values to the romanDigits series of numerals.

if (digit >= 5 && digit <= 8) { // Adds a V|L|D Roman numeral to string as needed string = romanDigits[1 + power * 2]; digit = digit - 5; } if (digit >=1 && digit < 4) { // Adds one, two, or three I|X|C|M Roman numerals to string as needed for (i = 0; i < digit; i++) string = string + romanDigits[0 + power * 2]; } if (digit == 4 || digit == 9) { string = romanDigits[0 + power * 2]; if (digit == 4) string = string + romanDigits[1 + power * 2]; else string = string + romanDigits[2 + power * 2]; }

Returning to our 374CCCLXXIV example and recalling that romanDigits = ["I", "V", "X", "L", "C", "D", "M"]:

(4) If digit is 4 and power is 0:
if (digit == 4 || digit == 9) { string = romanDigits[0 + power * 2]; sets string to I.
if (digit == 4) string = string + romanDigits[1 + power * 2]; appends a V to the I to give string = IV.

(70) If digit is 7 and power is 1:
if (digit >= 5 && digit <= 8) { string = romanDigits[1 + power * 2]; sets string to L.
digit = digit - 5; drops digit to 2.
if (digit >=1 && digit < 4) { for (i = 0; i < digit; i++) string = string + romanDigits[0 + power * 2]; appends two Xs to the L to give string = LXX.

(300) If digit is 3 and power is 2:
if (digit >=1 && digit < 4) { for (i = 0; i < digit; i++) string = string + romanDigits[0 + power * 2]; sets string to CCC.

The arabicToRomanDigit( ) function concludes with a return string; statement that returns string to the arabicToRoman( ) function.

Assembly and output

Back at the arabicToRoman( ) function, string (i.e., the arabicToRoman( ) string - note that the arabicToRoman( ) string and the arabicToRomanDigit( ) string are both local variables) is appended to the arabicToRomanDigit( ) return and the resulting string is assigned to string.

string = arabicToRomanDigit(digit, j) + string;

For the three for (j = 0; number > 0; j++) loop iterations, string goes from an empty string to IV to LXXIV to CCCLXXIV. The order of concatenation operands is important: a string += arabicToRomanDigit(digit, j); assignment would give a 'reversed' string = IVLXXCCC.

When the loop has finished executing, a return sign + string; statement appends string to sign and returns the resulting string to the document.write( ) command, which then writes

374 in Roman Numerals is CCCLXXIV

to the page. If it were up to me we would send string to an empty <div id="romanDiv"><div> via a

document.getElementById("romanDiv").textContent = "374 in Roman numerals is: " + string;

command, but to each his own.

The date

The script presents the following code for writing a date in Roman numerals:

var month = new buildArray("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
var today = new Date( );
var yr = today.getYear( );
if (yr < 100) yr = yr + 1900;
document.write("<br>Today is ", arabicToRoman(today.getDate( )), " ", month[today.getMonth( )], " ", arabicToRoman(yr));


I see you rolling your eyes. "getYear( )??" Yeah, that's the reason why the year in the date demo at the javascript/RomanNumerals.html page is off. Setting yr to today.getFullYear( ) sorts out the situation.

Sample output:
Today is VII September MMXIV

Demo

As promised, we end with a demo.

Input an Arabic integer in the range 1-3999 inclusive:




A !/^\d+$/.test(number) gate weeds out negative numbers, floating-point numbers, and nonnumeric inputs.

Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)