JS float causing incorrect rounding

I have something that could be an edge script. When I try to round the value 4.015 to two decimal places, I always get 4.01 instead of the expected 4.02 . This happens sequentially for all numbers with .015 as the decimal part.

I use a fairly common method in JS:

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

I think the problem starts when multiplied by 100. Inaccuracy with a floating point causes this value to be rounded, not 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 occur if I try to round 4.025 . The expected value of 4.03 returns; this is only a problem with .015 (for now).

Is there a way to elegantly resolve this? Of course, the hack just searches for .015 and handles this case one-time, but it just seems wrong!

+4
source share
4 answers

I ended up using math.js to do the math and solved all my floating point problems.

The advantage of this library was that it was not necessary to instantiate any Big Decimal object (although lib supports BigDecimal). It was as simple as replacing Math with Math and conveying precision.

+1
source

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

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

Thus, when dealing with floating point numbers, you should be aware that you will not have the same amount that you had in mind.
If you need an exact number, you should use a library that gives you better accuracy, usually it will use a fixed point and / or symbolic representation

More information can be found in wikipedia pag e and in this (slightly complicated, but important) article: What every computer scientist needs to know about floating point arithmetic

+2
source

If you are going to work with numbers as decimal numbers, use a decimal library like big.js.

Floating point values ​​in most languages ​​(including javascript) are stored in binary representation. Basically, it does what you expect. In such cases, your 4.015 is converted to a binary string and, as it turns out, gets the encoding as the value 4.014999999 ..., which you saw is the closest binary representation available in the IEEE754 value with double precision (8 bytes).

If you are doing financial or math for human consumption (i.e. as decimal numbers), you will need to round 4.015 to 4.02, and you will need a decimal library.

It is planned to include the decimal representation of floating point values ​​in javascript (e.g. here ), as the new IEEE754-2008 standard includes decimal32, etc. as a decimal representation of floating point values. Read more here: http://speleotrove.com/decimal/

Finally, if you perform mathematical calculations in javascript (for example, financial calculations that should not accidentally create or disappear money), please do all calculations in whole cents / pence.

+1
source

You can use regex to extract and replace digits to get what you want:

 val = (val + "").replace(/^([0-9]+\.[0-9])([0-9])([0-9]).*$/, function(whole, head, lastdigit, followup) { if(followup >= 5) { return head + ("" + (parseInt(lastdigit) + 1)); }else return head + lastdigit; }); 

Otherwise, you can use val = val.toFixed(2) , but a value of 4015 gives 4.01 (4.0151 gives 4.02 as "expected").

0
source

Source: https://habr.com/ru/post/1496103/


All Articles