View Full Version : help with safe decimal maths

code-in-time

05-30-2012, 05:17 AM

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.html

Philip M

05-30-2012, 07:22 AM

Use .toFixed(3).

<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 IEEE-754 (IEC 559) Doubles, with a resolution of 53 bits, giving an accuracy of 15-16 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:-

<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.

felgall

05-30-2012, 10:27 AM

I have read that doing maths in JavaScript if decimals and rounding are involved is not accurate.

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).

Old Pedant

05-30-2012, 10:26 PM

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...

STILL doesn't GUARANTEE "accuracy".

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 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).

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.

felgall

05-30-2012, 11:02 PM

STILL doesn't GUARANTEE "accuracy".

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.

Old Pedant

05-30-2012, 11:35 PM

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:

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.

If we use your (and Philip's) suggestion, you would have him do:

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);

Now c1 is (precisely) 15200343843614.829615

And so, in this example (with only 14 integer digits), the trick works.

But read his sentence just prior to the example:

a and b below might not always have the same number of decimal places

It would only take his a and b values being two orders of magnitude larger to get a precise value of

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:

If at ANY POINT during the calculations the integer part of your result exceeds 9e15, you are hosed.

I think that *must* be stated as the ruling limitation of the "multiply by the number of significant digits" method.

felgall

05-31-2012, 03:46 AM

No, I don't think I did introduce a new issue. I think you avoided a complete answer to the original question.

You did introduce a new issue - the half of the problem that neither Phillip nor I had considered in providing our answers - since it wasn't as clearly stated in the question as the half that we did answer.

Lerura

05-31-2012, 04:26 AM

How do I do this accurately?

Guess it means that OP want the result to be as accurate as possible.

AFAIK inaccuracy is only a problem with division. -> Where a solution similar to Old P's would be a good choice

Philip M

05-31-2012, 07:39 AM

You did introduce a new issue - the half of the problem that neither Phillip nor I had considered in providing our answers - since it wasn't as clearly stated in the question as the half that we did answer.

Actually, I thought that I had dealt with that issue:-

ECMAScript numbers are represented in binary as IEEE-754 (IEC 559) Doubles, with a resolution of 53 bits, giving an accuracy of 15-16 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.

Or, as Old Pedant put it, "if at ANY POINT during the calculations the integer part of your result exceeds 9e15, you are hosed."

felgall

05-31-2012, 08:17 AM

Actually, I thought that I had dealt with that issue:-

You're right. I was the only one who wasn't thinking about that half of the problem. There isn't a JavaScript solution to that part of the problem though - you'd need to swap to a language that supports larger numbers to get around that one.

Powered by vBulletin® Version 4.2.2 Copyright © 2017 vBulletin Solutions, Inc. All rights reserved.