Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS floating point causing incorrect rounding

I have what may be an edge case scenario. When trying to round the value 4.015 to 2 decimal places, I always end up with 4.01 instead of the expected 4.02. This happens consistently for all numbers with .015 as the decimal portion.

I round using a fairly common method in JS:

val = Math.round(val * 100) / 100;

I think the problem starts when multiplying by 100. The floating point inaccuracy causes this value to be rounded down rather than up.

var a = 4.015,                // 4.015
    mult = a * 100,           // 401.49999999999994 (the issue)
    round = Math.round(mult), // 401
    result = round / 100;     // 4.01 (expected 4.02)

Fiddle: http://jsfiddle.net/eVXRL/

This problem does not happen if I try to round 4.025. The expected value of 4.03 does return; it's only an issue with .015 (so far).

Is there a way to elegantly resolve this? There is of course the hack of just looking for .015 and handling that case one-off, but that just seems wrong!

like image 948
anushr Avatar asked Aug 09 '13 08:08

anushr


People also ask

Can floating point operations cause round off errors?

Even if some numbers can be represented exactly by floating-point numbers and such numbers are called machine numbers, performing floating-point arithmetic may lead to roundoff error in the final result.

What causes floating point rounding errors?

Because floating-point numbers have a limited number of digits, they cannot represent all real numbers accurately: when there are more digits than the format allows, the leftover ones are omitted - the number is rounded.

Why are floating point numbers inaccurate in JavaScript?

Because often-times, they are approximating rationals that cannot be represented finitely in base 2 (the digits repeat), and in general they are approximating real (possibly irrational) numbers which may not be representable in finitely many digits in any base.

How do I stop my floating from rounding?

you need to include <iomanip> and use the std::setprecision manipulator. To get the level of accuracy you want you will need to use double s rather than float s.


2 Answers

I ended up using math.js to do mathematical operations and that solved all my floating point issues.

The advantage of this lib was that there was no need to instantiate any sort of Big Decimal object (even though the lib does support BigDecimal). It was just as simple as replacing Math with math and passing the precision.

like image 97
anushr Avatar answered Sep 25 '22 21:09

anushr


Floating point numbers are not real numbers, they are floating point numbers.

There are infinite number of real numbers, but only finite number of bits to represent them, thus sometimes, there must be some rounding error if the exact number you want cannot be represented in the floating point system.

Thus, when dealing with floating point numbers, you must take into consideration, that you won't have the exact same number you had in mind.
If you need an exact number, you should use a library that gives you better precision, usually it will be using a fixed point, and/or symblic representation

More information can be found in the wikipedia page, and in this (a bit complex, but important) article: What Every Computer Scientist Should Know About Floating-Point Arithmetic

like image 41
amit Avatar answered Sep 25 '22 21:09

amit