When doing mathematical operations with floating point numbers in Javascript, the result appears to be incorrect and a fraction less than the expected result. This is a inherent consequence of how computers do floating point operations. To fix the problem, numbers need to be rounded to the right precision.
The input form below illustrates the problem on some browsers. (Internet Explorer on Mac OSX 10.3 displays the result correctly!)
<form action="javascript:;" method="post" onsubmit="setSum(this)" id="form1"> <input class="right" type="text" name="num1" onchange="checkIfNumber(document.forms.form1.num1)" /> <input class="right" type="text" name="num2" onchange="checkIfNumber(document.forms.form1.num2)" /> <input type="submit" name="submit" value="Add" /> <input type="button" name="reset" value="Reset" onclick="getDefaults(document.forms.form1)" /> Result: <input class="right" type="text" name="result" readonly="readonly" /> </form>
<script type="text/javascript"> // <![CDATA[ // default values to demonstrate the problem of number formats var num1default = 12.01; var num2default = 0.20; // set default values to input fields 'onload' function setDefaults(forms) { for (var i = 0; i < forms.length; ++i) { getDefaults(forms[i]); } return true; } // restore default values to to form input fields function getDefaults(formIn) { formIn.num1.value = num1default; formIn.num2.value = num2default; formIn.result.value = ""; } // set the result of addition to readonly input field 'result' of the form function setSum(formIn) { formIn.result.value = parseFloat(formIn.num1.value) + parseFloat(formIn.num2.value); } // check that a number entered into the form is indeed a valid number; // if not, set the value to 0.0 function checkIfNumber(num) { num.value = num.value.replace(/^\s*(\S+)\s*$/g, "$1"); if (isNaN(num.value) || num.value.match(/\S+/) == null) { num.value = "0.0"; } return true; } // ]]> </script>
Javascript contains a rounding function. However, it rounds to the next integer and thus drops the decimal points. To avoid losing them, the number may be multiplied by a power of a 10, rounded, and then divided by the same power of 10.
The following code block illustrates a general solution to decimal rounding. The function that performs the rounding also expects an integer as an input parameter. The integer indicates the number of decimal points. Hence the number to be rounded is multiplied and divided by 10(input parameter) to get the desired number of decimal points.
The function is a general solution to the problem since it works with any number of decimal points, not just one or two. The number of decimal points is here controlled by the HTML form, which provides four choices from 0
to 3
. The function does not need to be changed if more or fewer decimal points are desired.
<form action="javascript:;" method="post" onsubmit="roundSum(this, this.decimals.value)" id="form2"> <input class="right" type="text" name="num1" onchange="checkIfNumber(document.forms.form2.num1)" /> <input class="right" type="text" name="num2" onchange="checkIfNumber(document.forms.form2.num2)" /> Round to <select name="decimals"> <option value="0" selected="selected">0</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> </select>decimal points. <input type="submit" name="submit" value="round( )" /> <input type="button" name="reset" value="Reset" onclick="getDefaults(document.forms.form2)" /> Result: <input class="right" type="text" name="result" readonly="readonly" /> </form>
<script type="text/javascript"> // <![CDATA[ // use Javascript function round() on addition result; // parameter decimals is an integer value function roundSum(formIn, decimals) { // make sure parameter decimals is not a bogus value decimals = parseInt(decimals); if (isNaN(decimals)) { decimals = 0; } // make sure parameter decimals is a positive integer; // dPoints will be a power of 10 var dPoints = Math.pow(10, Math.abs(decimals)); // 'Math' is a Javascript object that // contains mathematical functions // steps from innermost parentheses outward: // 1. turn input parameters into floating point numbers // 2. perform addition // 3. multiply addition result by var dPoints // 4. round the result of multiplication to next integer value // 5. divide result of rounding by var dPoints // 6. display result of division in form input field formIn.result.value = Math.round((parseFloat(formIn.num1.value) + parseFloat(formIn.num2.value))*dPoints)/dPoints; return true; } // ]]> </script>
0
-PaddingRounding a number to a given number of decimal points does not always solve the problem of getting the right display format. When dealing with currency values, it is preferred to have two decimal points. However, when the last digit is a 0
, it is usually dropped. The following code example demonstrates a function that turns a number into a string and adds 0
s if necessary.
<form action="javascript:;" method="post" onsubmit="roundSum(this, this.decimals.value); doPadding(this, this.decimals.value)" id="form3"> <input class="right" type="text" name="num1" onchange="checkIfNumber(document.forms.form3.num1)" /> <input class="right" type="text" name="num2" onchange="checkIfNumber(document.forms.form3.num2)" /> Round to <select name="decimals"> <option value="0" selected="selected">0</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> </select>decimal points. <input type="submit" name="submit" value="round( )" /> <input type="button" name="reset" value="Reset" onclick="getDefaults(document.forms.form3)" /> Result: <input class="right" type="text" name="result" readonly="readonly" /> </form>
<script type="text/javascript"> // <![CDATA[ function doPadding(formIn, numOfDecimals) { // store integer value of 'result' value in var num var num = parseInt(formIn.result.value); // store decimals of 'result' value in var dec var dec = formIn.result.value.substr((formIn.result.value.indexOf('.')+1)); // check if 'result' value was an integer with no decimals; // if so, set var dec to "0" string as the first decimal of 0-padding if (formIn.result.value.indexOf('.') == -1) { dec = "0"; } // we assume value of parameter numOfDecimals is indeed a number; // it has been checked roundSum(); // add zero to dec string if string length is < numOfDecimals if (parseInt(numOfDecimals) != 0) { while (dec.length < numOfDecimals) { dec += "0"; // performs string concatenation } formIn.result.value = num + "." + dec.substr(0, numOfDecimals); } return true; } // ]]> </script>