Enjoy an ad free experience by logging in. Not a member yet? Register.


Results 1 to 10 of 10
Thread: help with safe decimal maths

05302012, 05:17 AM #1
 Join Date
 Mar 2012
 Posts
 60
 Thanks
 65
 Thanked 0 Times in 0 Posts
help with safe decimal maths
Hi
I have read that doing maths in JavaScript if decimals and rounding are involved is not accurate.
So how would you approach this to have an accurate answer every time.
a and b below might not always have the same number of decimal places but the answer must always have 3 decimal places
var a = 123.12345
var b = 123456.1234567
var c = a * b
var d = a / b
The answer of c and d must always have three decimal places.
How do I do this accurately?
Thanks for the help
PS is this accurate:
http://www.mredkj.com/javascript/nfbasic2.htmlLast edited by codeintime; 05302012 at 05:36 AM.
05302012, 07:22 AM
#2
 Join Date
 Jun 2002
 Location
 London, England
 Posts
 17,732
 Thanks
 202
 Thanked 2,508 Times in 2,486 Posts
Use .toFixed(3).
Code:<script type = "text/javascript"> var a = 123.12345 var b = 123456.1234567 var c = (a*b).toFixed(3); // 15200343.844 alert (c); var d = (a/b).toFixed(3); // 0.001 alert (d); </script>
ECMAScript numbers are represented in binary as IEEE754 (IEC 559) Doubles, with a resolution of 53 bits, giving an accuracy of 1516 decimal digits; integers up to just over 9e15 are precise, but few decimal fractions are. Given this, arithmetic is as exact as possible, but no more. Operations on integers are exact if the true result and all intermediates are integers within that range.
If you need an exact answer to a set number of decimal places then shift the decimal point in all your numbers that many places to the right to make integers before doing the calculation and then shift it back after the calculation. For example with currencies that use two decimal places you should always multiply them all by 100 at the start and divide by 100 at the end or the answer might not be exact.
Example:
Code:<script type = "text/javascript"> var a = 0.06; var b = 0.01; var c = a + b; alert (c); //0.06999999999999999 alert (c.toFixed(2)); // 0.07  Note that the output of .toFixed() is a string, not a number; var d = ((a*100) + (b*100))/100; alert (d); // 0.07 </script>
All advice is supplied packaged by intellectual weight, and not by volume. Contents may settle slightly in transit.
Last edited by Philip M; 05302012 at 07:48 AM.
All the code given in this post has been tested and is intended to address the question asked.
Unless stated otherwise it is not just a demonstration.
Users who have thanked Philip M for this post:
codeintime (06082012)
05302012, 10:27 AM
#3
 Join Date
 Sep 2005
 Location
 Sydney, Australia
 Posts
 6,472
 Thanks
 0
 Thanked 634 Times in 624 Posts
Substitute any other programming language for JavaScript in that statement and the statement will still be true. The only difference with JavaScript is because it has weak typing it doesn't treat 3.0 any differently from 3 and so doesn't have the problem where the numbers can be converted to integers.
It happens because computers work in binary so they convert your number to the nearest binary equivalent first and then convert the binary back into the nearest decimal equivalent at the end.
As Phillip said  the solution is to move the decimal point at the start and move it back at the end  so for three decimal places multiply everything by 1000 first, then do the calculations, then work out how many places to move the decimal point back at the end (divide by 1000 if just adding and subtracting, then add up the mumber of multiplications and subtract the number of divisions and further divide by 1000 that many more times).
Stephen
Learn Modern JavaScript  http://javascriptexample.net/
Helping others to solve their computer problem at http://www.felgall.com/
Don't forget to start your JavaScript code with"use strict";
which makes it easier to find errors in your code.
Users who have thanked felgall for this post:
codeintime (06082012)
05302012, 10:26 PM
#4
STILL doesn't GUARANTEE "accuracy".
If at ANY POINT during the calculations the integer part of your result exceeds 9e15, you are hosed. You cannot at all be sure your final result will be accurate to 3 decimal places (or even, in the above example, to 1 decimal place).Code:var x = 1234567890123456.888; var y = 987.000; var x1 = x * 1000; var x2 = y * 1000; var z1 = x1 * y1; var z = z1 / 1000000; var answer = z.toFixed(3);
If you are working with amounts of money, other than the amount of the US National Debt or something on that order, the "trick" works. But it simply doesn't work if you need answers where lots of digits are involved.
An optimist sees the glass as half full.
A pessimist sees the glass as half empty.
A realist drinks it no matter how much there is.
Users who have thanked Old Pedant for this post:
codeintime (06082012)
05302012, 11:02 PM
#5
 Join Date
 Sep 2005
 Location
 Sydney, Australia
 Posts
 6,472
 Thanks
 0
 Thanked 634 Times in 624 Posts
You have introduced a completely different issue to the discussion.
What was being discussed is how the fact that the computer using binary means that you can't add 0.1 and 0.2 on a computer and get an answer of 0.3 (unless you apply rounding).
Numbers overflowing the number of significant binary digits that the computer makes available to store each number in is a completely separate issue with all its own completely separate set of problems. That particular problem can occur just as easily with integers as it can with fractions.
Stephen
Learn Modern JavaScript  http://javascriptexample.net/
Helping others to solve their computer problem at http://www.felgall.com/
Don't forget to start your JavaScript code with"use strict";
which makes it easier to find errors in your code.
Users who have thanked felgall for this post:
codeintime (06082012)
05302012, 11:35 PM
#6
No, I don't think I did introduce a new issue. I think you avoided a complete answer to the original question.
Because his original question was based on this:
If we use your (and Philip's) suggestion, you would have him do:Code:var a = 123.12345 var b = 123456.1234567 var c = a * b var d = a / b The answer of c and d must always have three decimal places.
Now c1 is (precisely) 15200343843614.829615Code:var a = 123.12345; var b = 123456.1234567; var a1 = 123123.45; // by multiplying a * 1000 var b1 = 123456123.4567; // simiilarly var c1 = a1 * b1; var c = c1 / 1000000; var answer = c.toFixed(3);
And so, in this example (with only 14 integer digits), the trick works.
But read his sentence just prior to the example:It would only take his a and b values being two orders of magnitude larger to get a precise value ofa and b below might not always have the same number of decimal places
152003438436148296.15
which is 18 significant digits and so his result, to 3 decimal places, could *NOT* be guaranteed to be correct.
So I think my answer is quite germane to his original question.
**********
Please note what I stated:I think that *must* be stated as the ruling limitation of the "multiply by the number of significant digits" method.If at ANY POINT during the calculations the integer part of your result exceeds 9e15, you are hosed.
An optimist sees the glass as half full.
A pessimist sees the glass as half empty.
A realist drinks it no matter how much there is.
Users who have thanked Old Pedant for this post:
codeintime (06082012)
05312012, 03:46 AM
#7
 Join Date
 Sep 2005
 Location
 Sydney, Australia
 Posts
 6,472
 Thanks
 0
 Thanked 634 Times in 624 Posts
Stephen
Learn Modern JavaScript  http://javascriptexample.net/
Helping others to solve their computer problem at http://www.felgall.com/
Don't forget to start your JavaScript code with"use strict";
which makes it easier to find errors in your code.
Users who have thanked felgall for this post:
codeintime (06082012)
05312012, 04:26 AM
#8
Users who have thanked Lerura for this post:
codeintime (06082012)
05312012, 07:39 AM
#9
 Join Date
 Jun 2002
 Location
 London, England
 Posts
 17,732
 Thanks
 202
 Thanked 2,508 Times in 2,486 Posts
Actually, I thought that I had dealt with that issue:
Or, as Old Pedant put it, "if at ANY POINT during the calculations the integer part of your result exceeds 9e15, you are hosed."ECMAScript numbers are represented in binary as IEEE754 (IEC 559) Doubles, with a resolution of 53 bits, giving an accuracy of 1516 decimal digits; integers up to just over 9e15 are precise, but few decimal fractions are. Given this, arithmetic is as exact as possible, but no more. Operations on integers are exact if the true result and all intermediates are integers within that range.
Last edited by Philip M; 05312012 at 08:15 AM. Reason: Typo
All the code given in this post has been tested and is intended to address the question asked.
Unless stated otherwise it is not just a demonstration.
Users who have thanked Philip M for this post:
codeintime (06082012)
05312012, 08:17 AM
#10
 Join Date
 Sep 2005
 Location
 Sydney, Australia
 Posts
 6,472
 Thanks
 0
 Thanked 634 Times in 624 Posts
Last edited by felgall; 05312012 at 08:19 AM.
Stephen
Learn Modern JavaScript  http://javascriptexample.net/
Helping others to solve their computer problem at http://www.felgall.com/
Don't forget to start your JavaScript code with"use strict";
which makes it easier to find errors in your code.
Users who have thanked felgall for this post:
codeintime (06082012)