Chapter 17
Script Example 3: Bank Loan Calculator
In This Chapter
Math on the Web?
Web pages have been, for the most part, pretty pictures and tons of text. That's nice if all you want to do is make information available to the rest of the CyberCommunity, but for business applications, this can limit you. Previously, if you wanted to have the user interact with your pages (more than simply clicking on links and form buttons), you had to master the CGI interface (which opened a whole new world of frustrations).
With the invention of JavaScript, your pages can now do more than just display informationyou can create custom information tailored to the user. You can even have your pages do mathematical calculations. The example you will look at in this chapter is a loan calculator that computes the monthly payments for a given amount of money at a particular compound interest rate, and then displays an amortization table showing what the principal and interest components are for each month's payment. This is the kind of math you'd have to deal with if you were financing a car or a home mortgage.
Why a calculator? If you were an insurance salesman (for instance), you could have your site compute the premium a person would have to pay for a particular policy. Instead of having to set up an appointment with a potential client, pitch them on the benefits of your program, and haul your laptop computer to their home to construct and print a policy; you can make your company information available on the Web. Let your users browse your policy options, make selections, and discover right there what they would have to pay. Then, if they're interested, they can send you email to go to the next step. Makes your job easier, doesn't it (and it beats "cold calling")?
Before you dive into setting up the calculator, you need to take a look at some of the math functions that are built into JavaScript through the Math object. You can do most of what you need to with the Math object, but it does have one minor drawback: it doesn't have a method to round to the nearest penny.
Before you tackle this problem, quickly review what you can do with JavaScript math.
JavaScript Algebra 101
JavaScript's math functions provide some powerful flexibility to your scripts. Aside from the basic operations (+, , /, and *), the Math object has additional methods that perform most of the special functions you find on today's calculators: abs(), acos(), asin(), atan(), cos(), exp(), log(), pow(), sin(), sqrt(), and tan(). Another method, random(), even generates a random number (you'll play with that in the next chapter). Additionally, parentheses allow you to group various substeps of a math equation to control what part of the equation is calculated first. Numbers in an expression are called constants, and variables are the letters (or words) that represent another value that changes depending on what's done to it.
If this looks like introductory algebra, it should; You can write JavaScript math expressions the same way you write algebra expressions. For example, adding two numbers together and assigning the result to a variable is done in algebra by:
a = 3 + 5
while in JavaScript, you would type:
a = 3 + 5;
The only real difference is the addition of the semicolon to indicate the end of the expression.
JavaScript math is decimal math. Any math operations performed (whether or not there are integer constants involved) are converted to decimal and the decimal is computed. So, an expression like:
a = 9 / 2;
would evaluate (a fancy word for "the answer is") to:
a = 4.5
This is all well and good, but what about numbers that don't produce simple decimals? If you were to type in the simple page:
<HTML>
<BODY>
</BODY>
<SCRIPT LANGUAGE="JavaScript">
<!
document.write("100 divided by 3 is " + (100 / 3));
//>
</SCRIPT>
</HTML>
and view it with your browser, you'd find the result displayed on the Web page.
That Last Decimal Is Wrong!

Yep, it is. Computers aren't really good with fractions. You and I know that 1.3333333333_ is the same as 1/3, but a computer needs to be accurate, and 1/3 as a decimal doesn't ever end. So, the computer does its best to calculate the fraction out as far as it can (called number of digits precision). Because a computer isn't an infinite thing (it has a fixed amount of memory, its processor chip is only so big, and so on), it has a fixed number of digits precision (in this case, 14 digits).
After the 14th digit, if the fraction is still going, the computer pretty much gives up. What it does next is basically anybody's guess: round up, round down, pick a number at random_anything. Because of this, the last decimal is wrong, and although there are 15 decimal places, only 14 of them are precise.

That's 15 decimal places… plenty when you're doing something scientific, but way too many decimal places for most uses.
In this case (dealing with dollars and cents), you need only two decimal places. Anything after that should be dropped, not rounded (money is one area where everyone would love to round up to the nearest dollar_but it's just not done unless you're filling out a tax form). Unfortunately, there is no method in the Math object that allows you to automatically chop off those fractions of a penny. To do this, you need to "roll your own" function, using the round() method of the Math object as a basis.
Dollars and Decimals
Because JavaScript math would have you working with thousandths of a penny, you need to create a function that causes all those little copper shavings to be swept under the rug. To do that, you'll need to use the round() method of the Math object.
The round() method does just what its name implies: it rounds a decimal number to the nearest whole number. Using the earlier example, if you want to round the result of 100 divided by 3, you would type:
a = Math.round(100 / 3);
You can check this with another little short page:
<HTML>
<BODY>
</BODY>
<SCRIPT LANGUAGE="JavaScript">
<!
document.write("100 divided by 3 (rounded) is " + Math.round(100 / 3));
//>
</SCRIPT>
</HTML>
And you'd see:
100 divided by 3 (rounded) is 33
displayed in your browser. Using round() causes all the digits to the right of the decimal to be swept away and, if necessary, the number to the left is rounded up (so if you were rounding 4.6, you'd get 5). This gets half the job done (using your money example, you're rounding to the nearest dollar), but you want to keep two decimal places (the pennies), so you need to do a bit more math.
Since you want round() to round to the nearest penny, you need to convert the number from dollars and cents to just cents. You can do this by multiplying the dollar amount by 100 (if you have 1 dollar, you have 100 pennies), and then performing the round. After that, you just shift the decimal point to the left two places (divide by 100) and you have a dollar and cents amount. Simple, right?
Not quite. Remember that computers aren't very good with dividing fractions. If you were to type
a = 100 / 3
pennies = a * 100
document.write("100 divided by 3 is " + Math.round(pennies) / 100);
as a script, you'd see
100 divided by 3 is 33.329999999999998
displayed in your browser. Still not right, but you're getting closer. Replacing the document.write() line above with
document.write("100 divided by 3 (in pennies) is " + Math.round(pennies));
would cause
100 divided by 3 (in pennies) is 3333
to print out, so you are figuring the right number of pennies. The problem is the computer's inability to handle decimal division precisely.
To fix this, you need to take a different approach and just print out the number of pennies with a decimal point stuck in the middle. You can do this with JavaScript because you can take any number and convert it to a string:
strPennies = "" + pennies;
The "" put in front of pennies causes the conversion, and strPennies becomes a string version of the value in pennies.
Now, to plug a decimal point into the middle of strPennies, construct another string that consists of:
 All the digits in strPennies but the last two.
 A decimal point.
 The last two digits from strPennies.
Remember from Chapter 6 that the string object in JavaScript has a substring() method, with which you can take a piece of a larger string. If you use a variable len to hold the number of digits in strPennies, all but the last two digits in strPennies would be
strPennies.substring(0, len  2)
and the last two digits would be
strPennies.substring(len  2, len)
Remember that the first parameter of substring() is the character position to start at and the second parameter is the position to end before (in the computer world, you start counting character positions at 0 instead of 1). Putting your number together:
strPennies.substring(0, len  2) + "." + strPennies.substring(len  2, len);
This takes 33.329999999999998 and turns it into 33.33. Putting it all together in a function:
function roundToPennies(n)
{
pennies = n * 100;
pennies = Math.round(pennies);
strPennies = "" + pennies;
len = strPennies.length;
return strPennies.substring(0, len  2) + "." + strPennies.substring(len  2, len);
<CX>}
You now have a function that rounds to dollars and cents! You could extend this function to round to any number of decimal places (up to 14 decimals, at which point the computer loses accuracy), but I'll leave that as an exercise. Now, you can build the function that computes the monthly payment.
The Monthly Payment
If you've ever borrowed money (from a bank, not your uncle), you've had to pay interest, a percentage of "profit" the bank makes for letting you use its money. Being fond of math (as bank people are), the formula used to calculate the interest on a bank loan isn't a simple one, and one look at the printout that comes with loan papers (showing what your monthly payment is and what part is principal and what part is interest) proves it.
The figures in the amortization table show that you pay mostly interest (the money the bank makes) for the beginning of the loan, and then more and more principal (the actual money you borrowed) as you go along. This is because the interest for a given month is computed against whatever the unpaid principal is. As you make more payments, the amount of remaining principal decreases, and the amount of interest computed decreases. Since you are paying a fixed amount each month, less interest means more principal in each payment. Even banks make their money "up front."
If you look in a book on finance, you'll find that the formula for computing a monthly payment on an amortized loan is rather complex:
monthly = principal * rate / (1  (1 / (1 + rate)^payments ))
where:
Variable Meaning
monthly

Amount of monthly payment.

Principal

Total amount borrowed.

Rate

Monthly rate (annual rate divided by 12 months). For example, an APR (annual percentage rate) of 12% equals a monthly interest rate of 0.01 (1%/month).

Payments

Total number of monthly payments.

But, with JavaScript's Math object, this formula is easily tamed. The worst piece of it is:
(1 + rate)^payments
The pow() method is designed to handle just such a problem (remember that pow() takes two parameters, and raises the value of the first parameter to the power of the second). So, in JavaScriptese, this becomes:
Math.pow(1 + rate, payments);
Putting this together with the rest of the formula, you have:
monthly = principal * rate / (1  (1 / Math.pow(1 + rate, payments)));
And, you can now write your function:
function Monthly(principal, years, apr)
{
rate = apr / 12;
payments = years * 12;
return principal * rate / (1  (1 / Math.pow(1 + rate, payments)));
}
With this function complete, you have one last piece of your math puzzle to construct: for any given month, how much of the monthly payment is interest and how much is principal?
Amortization Tables
In an amortized loan, the interest incurred isn't computed all at once. Rather, the amount is figured out each payment period (in your example, each month) against whatever the principal is at the time, much the same way that you are charged interest on your credit card balance. Also, the amount of interest charged is the monthly interest rate (the annual rate divided by 12).
So, if you were to borrow $50,000 at 8% over 10 years (120 payments), the monthly payment would be $606.64, and the monthly interest rate would be 0.08 divided by 12, or 0.00666666666667 (remember, you can handle only 14 digits precision). At the end of the first month, the principal is still $50,000, so the interest incurred that first month would be
$50,000.00 * 0.00666666666667 = $333.35
The amount of your payment that is actually principal is what's left over after paying off the interest: $273.29. Therefore, at the end of the second month, the principal has been reduced:
$50,000.00  $273.29 = $49,726.71
This becomes the new principal, and the process continues until all the money has been paid back.
This can be done in JavaScript using a for loop that runs over the total number of payments:
for(i = 0; i < payments; i++)
{
interestPayment = principal * monthlyInterest;
principalPayment = monthlyPayment  interestPayment;
principal = principalPayment;
}
Notice the use of = in the loop. This is a nice shorthand for
principal = principal  principalPayment;
For each pass through the loop, principal is reduced by the amount of principalPayment (the part of the payment that is not interest). As principal gets smaller, so will interestPayment, meaning what's left over will increase_exactly what happens in an amortized loan.
Finally, you want to print out this information for each month. Using document.write(), you can write HTML statements right out to the current page (no need for CGI here!). Adding this to the for loop above gives you the monthly amortization function:
function MonthlyAmortization(principal, years, apr)
{
payments = years * 12;
monthlyInterest = apr / 12;
monthlyPayment = Monthly(principal, years, apr);
for(i = 1; i <= payments; i++)
{
document.write(i);
interestPayment = principal * monthlyInterest;
document.write("$" + roundtoPennies(interestPayment));
principalPayment = monthlyPayment  interestPayment;
document.write("$" + roundToPennies(principalPayment));
principal = principalPayment;
document.write("$" + roundToPennies(principal));
document.write("<BR>");
}
}
This prints one line per payment, displaying the payment number, the amount of interest paid, and the amount of principal paid. Using roundToPennies(), you make sure that only dollars and cents appear. Adding a dollar sign in front of each number makes it look even better.
The <BR> tag at the end forces a carriage return; otherwise, the displayed data would wrap around the screen. There is, however, a better way_and you'll look at that next.
Forms in a Function
A better method of presenting a table of information like this would be to use the <TABLE> tag in HTML to format your information automatically (saves having to compute widths and margins and all that ugly stuff). But, can you create a table dynamically? Yes, and you won't believe how easy it is.
Remember that the basic structure for an HTML table is
<TABLE>
<TR>
<TD>column 1</TD>
<TD>column 2</TD>
</TR>
</TABLE>
This displays a table of one row and two columns. Since the document.write() function allows you to write out HTML statements as well as regular (display) text, you can just as easily write out table control tags.
Look back at the MonthlyAmortization() function. Each pass through the for loop generates one row of information. This means that you can rewrite this loop with <TR> and <TD> tags:
for(i = 1; i <= payments; i++)
{
document.write("<TR>");
document.write("<TD>" + i + "</TD>");
interestPayment = principal * monthlyInterest;
document.write("<TD>$" + roundToPennies(interestPayment) + "</TD>");
principalPayment = monthlyPayment  interestPayment;
document.write("<TD>$" + roundToPennies(principalPayment) + "</TD>");
principal = principalPayment;
document.write("<TD>$" + roundToPennies(principal) + "</TD>");
document.write("</TR>");
}
This generates a table exactly the right size and makes sure everything lines up the way you want it to. The only thing left is to add a table heading and put your page together.
Amortized Loans The Whole Picture
With the individual pieces built, you can now construct the entire page:
<HTML>
<HEAD>
<TITLE>Loan Calculator</TITLE>
<SCRIPT LANGUAGE="LiveScript">
<! hide from nonJavaScript browsers
function roundToPennies(n)
{
pennies = n * 100;
pennies = Math.round(pennies);
strPennies = "" + pennies;
len = strPennies.length;
return strPennies.substring(0, len  2) + "." + strPennies.substring(len  2, len);
}
function Monthly(principal, years, apr)
{
rate = apr / 12;
payments = years * 12;
return roundToPennies(principal * rate / (1  (1 / Math.pow(1 + rate, payments))));
}
function MonthlyAmortization(principal, years, apr)
{
payments = years * 12;
monthlyInterest = apr / 12;
monthlyPayment = Monthly(principal, years, apr);
document.write("<CENTER>");
document.write("<H1>Amortization Table</H1>");
document.write("<HR>");
document.write("<TABLE BORDER>");
document.write("<TR>");
document.write("<TH COLSPAN=4>");
document.write("$" + roundToPennies(principal));
document.write(" at " + roundToPennies(apr) + "%");
document.write(" over " + years + " years.<BR>");
document.write("Monthly payment: $" + Monthly(principal, years, apr));
document.write("</TH>");
document.write("</TR>");
document.write("<TR>");
document.write("<TH></TH>");
document.write("<TH COLSPAN=2>Payment</TH>");
document.write("</TR>");
document.write("<TR>");
document.write("<TH>Month</TH>");
document.write("<TH>Interest</TH>");
document.write("<TH>Principal</TH>");
document.write("<TH>Balance</TH>");
document.write("</TR>");
for(i = 1; i <= payments; i++)
{
document.write("<TR>");
document.write("<TD>" + i + "</TD>");
interestPayment = principal * monthlyInterest;
document.write("<TD>$" + roundToPennies(interestPayment) + "</TD>");
principalPayment = monthlyPayment  interestPayment;
document.write("<TD>$" + roundToPennies(principalPayment) + "</TD>");
principal = principalPayment;
document.write("<TD>$" + roundToPennies(principal) + "</TD>");
document.write("</TD>");
}
document.write("</TABLE>");
document.write("</CENTER>");
}
function compute(form)
{
if((form.principal.value.length != 0) &&
(form.apr.value.length != 0) &&
(form.years.value.length != 0))
{
principal = eval(form.principal.value);
apr = eval(form.apr.value) / 100.0;
years = eval(form.years.value);
if(years == 0.0)
{
alert(
"You have no monthly payment, since the number of years is zero.");
}
else
{
MonthlyAmortization(principal, years, apr);
}
}
else
{
alert("You must fill in all the fields.");
}
}
//>
</SCRIPT>
</HEAD>
<BODY>
<CENTER><H1>Loan Calculator</H1></CENTER>
<HR>
<CENTER>
<FORM>
<CENTER>
Fill in the fields, then click
<INPUT TYPE=BUTTON VALUE="Amortize!" onClick=compute(this.form)>
</CENTER>
<P>
<TABLE BORDER=3>
<TR>
<TD>Amount of the loan ($)</TD>
<TD><INPUT TYPE=TEXT NAME=principal></TD>
</TR>
<TR>
<TD>Annual interest rate (%)</TD>
<TD><INPUT TYPE=TEXT NAME=apr></TD>
</TR>
<TR>
<TD>Total number of years</TD>
<TD><INPUT TYPE=TEXT NAME=years></TD>
</TR>
</TABLE>
</FORM>
</CENTER>
</BODY>
</HTML>
As you can see, this script also demonstrates the use of HTML tables to create a page that has a pleasing layout.
A loan amortizer in JavaScript.


Once the user clicks Amortize!, the monthlyAmortization() function goes to work, producing the amortization table. Additionally, you'll notice (when you try this in your browser) that this method of writing directly to the active page causes the page to be reloaded with the new data.
The amortization table produced by the calculator.


The Least You Need To Know
It Looks Different, But It's Still the Same Page

Any HTML statements that are written with document.write() aren't written back to your page. They are created by the browser when needed but are not saved back over your page on your site. This way, you can run the program again and again with different figures.

This chapter presented another math example: computing the monthly payment on a loan and producing an amortization schedule based on that information. You learned how JavaScript deals with decimal numbers and learned a way around JavaScript's lack of "number of decimal places" control. You also learned a way to format the page using HTML tables to create a pleasing layout, as well as how to get JavaScript to create a table for you dynamically… customizing your pages to the user's specific needs.
For comments or technical support for our books and software, select Talk to Us.
To order books, call us at 8007160044 or 3172284366.
© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon & Schuster Company.