I posted this code a couple days back, but the discussion seemed to wax philosophical about the weaknesses of Javascript (not to mention my own obvious weaknesses as a "programmer") and then died out without ever clarifying a solution. I'm hoping someone will help rectify that.
Due to a phenomenon apparently known as "floating point math," Javascript returns an arithmetically inaccurate answer (.00000004 or similar below the correct answer) in the calculator code below. I was advised to round the answer up by "calling math.round() on the variable," which I think would work fine for my purposes, only my JS kung fu was too weak, and the syntax of doing so in context has thus far eluded me.
Where/how to I make this call? So far all my attempts have failed, even when I thought for sure each would not. I would sure appreciate an answer that takes into account my low-level knowledge of the subject. This has got to be a slam-dunk for somebody out there.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ".dtd">
<html xmlns="">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untitled Document</title>
<script language="javascript">
<!-- Begin Trip Tickets Savings Calc script
function doMath4() {
var one = parseInt(document.theForm4.elements[0].value);
var two = parseInt(document.theForm4.elements[1].value);
var selection = document.getElementsByName("zonett")[0].value;
if(selection == "z4"){
var prodZ4tt = (((one * two) * 4.25) *12) - (((one * two) * 3.75) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodZ4tt + ".");
}
else if(selection == "z3"){
var prodZ3tt = (((one * two) * 3.75) *12) - (((one * two) * 3.35) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodZ3tt + ".");
}
else if(selection == "z2"){
var prodZ2tt = (((one * two) * 3) *12) - (((one * two) * 2.8) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodZ2tt + ".");
}
else if(selection == "z1"){
var prodZ1tt = (((one * two) * 2.5) *12) - (((one * two) * 2.3) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodZ1tt + ".");
}
else if(selection == "Base"){
var prodBasett = (((one * two) * 1.5) *12) - (((one * two) * 1.5) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodBasett + ".");
}
}
// End Trip Tickets Savings Calc script -->
</script>
</head>
<body>
<form name="theForm4" class="calcform">
<h2>You Do the Math: Commuter Express Trip Tickets Vs. Cash</h2>
<div class="calcform-content">
<div class="formrow-calc">
<div class="calcform-col1">
<p>Days you mute on Commuter Express monthly:</p>
</div>
<div class="calcform-col2">
<input type="text">
</div>
<div class="calcform-col3"> </div>
</div>
<div class="clear"></div>
<div class="formrow-calc">
<div class="calcform-col1">
<p>Daily boardings on Commuter Express Bus:</p>
<table border="0" cellspacing="0" cellpadding="0" class="fareexampletable">
<tr>
<td colspan="2" class="savingsleft"><p class="ifyouride">EXAMPLE:</p></td>
</tr>
<tr>
<td class="savingsleft"><p><strong>Go to work:</strong></p></td>
<td class="savingsright"><p>1 time</p></td>
</tr>
<tr>
<td class="savingsleft"><p><strong>Come home from work:</strong></p></td>
<td class="additionline savingsright"><p>+1 time</p></td>
</tr>
<tr>
<td class="savingsleft"><p><strong>Total:</strong></p></td>
<td class="savingsright"><p>2 times</p></td>
</tr>
</table>
</div>
<div class="calcform-col2">
<input type="text">
</div>
<div class="calcform-col3"> </div>
</div>
<div class="clear"></div>
<div class="formrow-calc savings-zone">
<div class="calcform-col1">
<p>Choose Zone:</p>
</div>
<div class="calcform-col2">
<select name="zonett">
<option value="Base">Base</option>
<option value="z1">Zone 1</option>
<option value="z2">Zone 2</option>
<option value="z3">Zone 3</option>
<option value="z4">Zone 4</option>
</select>
</div>
</div>
<div class="formrow-calc">
<div class="calcform-col4-ce">
<button type="submit" onclick="doMath4()" class="btn-submit"><div class="btn-submit"><img src="img/btn_savings.png" alt="Show My Yearly Savings" /></div></button>
</div>
</div>
<div class="clear">
</div>
</div>
</form>
</body>
</html>
I posted this code a couple days back, but the discussion seemed to wax philosophical about the weaknesses of Javascript (not to mention my own obvious weaknesses as a "programmer") and then died out without ever clarifying a solution. I'm hoping someone will help rectify that.
Due to a phenomenon apparently known as "floating point math," Javascript returns an arithmetically inaccurate answer (.00000004 or similar below the correct answer) in the calculator code below. I was advised to round the answer up by "calling math.round() on the variable," which I think would work fine for my purposes, only my JS kung fu was too weak, and the syntax of doing so in context has thus far eluded me.
Where/how to I make this call? So far all my attempts have failed, even when I thought for sure each would not. I would sure appreciate an answer that takes into account my low-level knowledge of the subject. This has got to be a slam-dunk for somebody out there.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untitled Document</title>
<script language="javascript">
<!-- Begin Trip Tickets Savings Calc script
function doMath4() {
var one = parseInt(document.theForm4.elements[0].value);
var two = parseInt(document.theForm4.elements[1].value);
var selection = document.getElementsByName("zonett")[0].value;
if(selection == "z4"){
var prodZ4tt = (((one * two) * 4.25) *12) - (((one * two) * 3.75) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodZ4tt + ".");
}
else if(selection == "z3"){
var prodZ3tt = (((one * two) * 3.75) *12) - (((one * two) * 3.35) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodZ3tt + ".");
}
else if(selection == "z2"){
var prodZ2tt = (((one * two) * 3) *12) - (((one * two) * 2.8) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodZ2tt + ".");
}
else if(selection == "z1"){
var prodZ1tt = (((one * two) * 2.5) *12) - (((one * two) * 2.3) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodZ1tt + ".");
}
else if(selection == "Base"){
var prodBasett = (((one * two) * 1.5) *12) - (((one * two) * 1.5) *12);
alert("Your yearly savings if you buy Trip Tickets is $" + prodBasett + ".");
}
}
// End Trip Tickets Savings Calc script -->
</script>
</head>
<body>
<form name="theForm4" class="calcform">
<h2>You Do the Math: Commuter Express Trip Tickets Vs. Cash</h2>
<div class="calcform-content">
<div class="formrow-calc">
<div class="calcform-col1">
<p>Days you mute on Commuter Express monthly:</p>
</div>
<div class="calcform-col2">
<input type="text">
</div>
<div class="calcform-col3"> </div>
</div>
<div class="clear"></div>
<div class="formrow-calc">
<div class="calcform-col1">
<p>Daily boardings on Commuter Express Bus:</p>
<table border="0" cellspacing="0" cellpadding="0" class="fareexampletable">
<tr>
<td colspan="2" class="savingsleft"><p class="ifyouride">EXAMPLE:</p></td>
</tr>
<tr>
<td class="savingsleft"><p><strong>Go to work:</strong></p></td>
<td class="savingsright"><p>1 time</p></td>
</tr>
<tr>
<td class="savingsleft"><p><strong>Come home from work:</strong></p></td>
<td class="additionline savingsright"><p>+1 time</p></td>
</tr>
<tr>
<td class="savingsleft"><p><strong>Total:</strong></p></td>
<td class="savingsright"><p>2 times</p></td>
</tr>
</table>
</div>
<div class="calcform-col2">
<input type="text">
</div>
<div class="calcform-col3"> </div>
</div>
<div class="clear"></div>
<div class="formrow-calc savings-zone">
<div class="calcform-col1">
<p>Choose Zone:</p>
</div>
<div class="calcform-col2">
<select name="zonett">
<option value="Base">Base</option>
<option value="z1">Zone 1</option>
<option value="z2">Zone 2</option>
<option value="z3">Zone 3</option>
<option value="z4">Zone 4</option>
</select>
</div>
</div>
<div class="formrow-calc">
<div class="calcform-col4-ce">
<button type="submit" onclick="doMath4()" class="btn-submit"><div class="btn-submit"><img src="img/btn_savings.png" alt="Show My Yearly Savings" /></div></button>
</div>
</div>
<div class="clear">
</div>
</div>
</form>
</body>
</html>
Share
Improve this question
asked May 11, 2011 at 20:49
user719431user719431
552 silver badges6 bronze badges
6
- 1 Where are you arriving at an inaccuracy? – 65Fbef05 Commented May 11, 2011 at 20:55
- @ 65Fbef05 Only when IO choose zones 1-3 – user719431 Commented May 11, 2011 at 21:09
- The solution is simple: Don't use floating point values in financial putation. Use cents for everything or multiple accordingly so that you don't have to work with floating point numbers... – Felix Kling Commented May 11, 2011 at 21:13
- @Felix Say Huh? Simple, you say? I clearly am out of my element here, but I'm seeing cent values in those admittedly awkward but still functional formulae. The solution may be simple, but I still haven't seen it. – user719431 Commented May 11, 2011 at 21:20
-
What I mean is that you should work on integers only and only for presentation create floating point values. So instead of
3.12 * 12
you have312 * 1200
and for presentation you divide it by100
again. – Felix Kling Commented May 11, 2011 at 21:30
4 Answers
Reset to default 2In general you want to round last, so you maintain mathematical accuracy up until the point that data is displayed.
Here is how I would implement your little library:
var SavingsLib = {
round : function(val, digits) {
var pow = Math.pow(10, digits);
return Math.round(pow * val) / pow;
},
padCurrency : function (val) {
var str = val.toString();
var parts = str.split(".");
return parts.length > 1 && parts[1].length == 1 ? str + "0" : str;
},
processForm : function() {
var daysPerMonth = parseInt(document.theForm4.elements[0].value);
var boardingsPerDay = parseInt(document.theForm4.elements[1].value);
var zone = document.getElementsByName("zonett")[0].value;
var savings = 0;
if(zone == "z4") savings = this.calculateSavings(daysPerMonth, boardingsPerDay, 4.25, 3.75);
else if(zone == "z3") savings = this.calculateSavings(daysPerMonth, boardingsPerDay, 3.75, 3.35);
else if(zone == "z2") savings = this.calculateSavings(daysPerMonth, boardingsPerDay, 3, 2.8);
else if(zone == "z1") savings = this.calculateSavings(daysPerMonth, boardingsPerDay, 2.5, 2.3);
else if(zone == "Base") savings = this.calculateSavings(daysPerMonth, boardingsPerDay, 1.5, 1.5);
this.showMessage(savings);
return false; // Don't submit form
},
calculateSavings : function(daysPerMonth, boardingsPerDay, price1, price2) {
var amount1 = (((daysPerMonth * boardingsPerDay) * price1) * 12);
var amount2 = (((daysPerMonth * boardingsPerDay) * price2) * 12);
return this.round(amount1 - amount2, 2);
},
showMessage : function(savings) {
alert("Your yearly savings if you buy Trip Tickets is $" + this.padCurrency(savings));
}
}
My changes:
- Encapsulated functions into an object for namespacing
- Extracted the math out into a single method so you can make one round call
- Extracted the showing of the message into a method so you can modify it easily
- Added a round-to-digits function (So
1.4500001
bees1.45
instead of1
) - Added a pad currency function (So
1.4
bees1.40
)
You can write a kludge with Math.round, but that will nuke cents (not sure if this applies in your example, it sort of looks like it could). What you may want is toFixed:
alert((102.000000004).toFixed(2)); // alerts "102.00"
This does NOT round in IE, but from what I can tell this will not matter in your case.
(docs: https://developer.mozilla/en/JavaScript/Reference/Global_Objects/Number/toFixed , http://www.codingforums./showthread.php?t=102421 , http://msdn.microsoft./en-us/library/sstyff0z.aspx )
Edit: intended to put this in the ments, but markdown doesn't work there? Still getting used to SO...
Anyhow, you can also use a kludge with Math.round if you prefer:
alert(Math.round(val * 100) / 100);
Which may still display the silly float issues, for which you could use another .toFixed (and now you're 100% sure there's no leftover decimal places you're not rounding properly):
alert((Math.round(val * 100) / 100).toFixed(2));
Edit: to integrate:
Add this at the top of your script block:
function roundNicely(val) {
return (Math.round(val * 100) / 100).toFixed(2);
}
Then in your alerts, wrap the value prodBasett
in the function call: roundNicely(prodBasett)
(and similar for the other alerts)
Does that make sense?
It's very simple:
var prodZ4tt = Math.round(((one * two) * 4.25) *12) - (((one * two) * 3.75) *12);
Although it's understandable why you might get confused about syntax, since JavaScript is not known for its intuitive way of doing things. ;)
Note: This rounds the number to the nearest integer, it doesn't "round up."
For that cases when you need precision, you must need an Math "Arbitrary Precision Library"... you could find a good article here.
http://blog.thatscaptaintoyou./introducing-big-js-arbitrary-precision-math-for-javascript/
Javascript natively have round, ceil and floor to convert floats to integers.
var fl = 349.12783691847;
Math.round(fl) // 349
Math.ceil(f1) // 350
Math.floor(fl) // 349
when you do math operations, is good practice use distributivity, and properly use of parenthesis.
Math.round(((((a + b) * (c - d)) % e) * f));
Be careful with "NaN" (http://en.wikipedia/wiki/NaN) and non Number types.
have a nice day