How to deal with floating point number precision i

2018-12-30 22:34发布

I have the following dummy test script:

function test(){
    var x = 0.1 * 0.2;
    document.write(x);
}
test();

This will print the result 0.020000000000000004 while it should just print 0.02 (if you use your calculator). As far as I understood this is due to errors in the floating point multiplication precision.

Does anyone have a good solution so that in such case I get the correct result 0.02? I know there are functions like toFixed or rounding would be another possibility, but I'd like to really have the whole number printed without any cutting and rounding. Just wanted to know if one of you has some nice, elegant solution.

Of course, otherwise I'll round to some 10 digits or so.

30条回答
孤独总比滥情好
2楼-- · 2018-12-30 22:40

You can't represent most decimal fractions exactly with binary floating point types (which is what ECMAScript uses to represent floating point values). So there isn't an elegant solution unless you use arbitrary precision arithmetic types or a decimal based floating point type. For example, the Calculator app that ships with Windows now uses arbitrary precision arithmetic to solve this problem.

查看更多
公子世无双
3楼-- · 2018-12-30 22:41

while adding two float value its never give the precise values so we need to fixed this to certain number that will help us to compare.

console.log((parseFloat(0.1) + parseFloat(0.2)).toFixed(1) == parseFloat(0.3).toFixed(1));
查看更多
呛了眼睛熬了心
4楼-- · 2018-12-30 22:43

To avoid this you should work with integer values instead of floating points. So when you want to have 2 positions precision work with the values * 100, for 3 positions use 1000. When displaying you use a formatter to put in the separator.

Many systems omit working with decimals this way. That is the reason why many systems work with cents (as integer) instead of dollars/euro's (as floating point).

查看更多
梦寄多情
5楼-- · 2018-12-30 22:44

You can use parseFloat() and toFixed() if you want to bypass this issue for a small operation:

a = 0.1;
b = 0.2;

a + b = 0.30000000000000004;

c = parseFloat((a+b).toFixed(2));

c = 0.3;

a = 0.3;
b = 0.2;

a - b = 0.09999999999999998;

c = parseFloat((a-b).toFixed(2));

c = 0.1;
查看更多
怪性笑人.
6楼-- · 2018-12-30 22:44

Problem

Floating point can't store all decimal values exactly. So when using floating point formats there will always be rounding errors on the input values. The errors on the inputs of course results on errors on the output. In case of a discrete function or operator there can be big differences on the output around the point where the function or operator is discrete.

Input and output for floating point values

So, when using floating point variables, you should always be aware of this. And whatever output you want from a calculation with floating points should always be formatted/conditioned before displaying with this in mind.
When only continuous functions and operators are used, rounding to the desired precision often will do (don't truncate). Standard formatting features used to convert floats to string will usually do this for you.
Because the rounding adds an error which can cause the total error to be more then half of the desired precision, the output should be corrected based on expected precision of inputs and desired precision of output. You should

  • Round inputs to the expected precision or make sure no values can be entered with higher precision.
  • Add a small value to the outputs before rounding/formatting them which is smaller than or equal to 1/4 of the desired precision and bigger than the maximum expected error caused by rounding errors on input and during calculation. If that is not possible the combination of the precision of the used data type isn't enough to deliver the desired output precision for your calculation.

These 2 things are usually not done and in most cases the differences caused by not doing them are too small to be important for most users, but I already had a project where output wasn't accepted by the users without those corrections.

Discrete functions or operators (like modula)

When discrete operators or functions are involved, extra corrections might be required to make sure the output is as expected. Rounding and adding small corrections before rounding can't solve the problem.
A special check/correction on intermediate calculation results, immediately after applying the discrete function or operator might be required. For a specific case (modula operator), see my answer on question: Why does modulus operator return fractional number in javascript?

Better avoid having the problem

It is often more efficient to avoid these problems by using data types (integer or fixed point formats) for calculations like this which can store the expected input without rounding errors. An example of that is that you should never use floating point values for financial calculations.

查看更多
荒废的爱情
7楼-- · 2018-12-30 22:45

I am not that much good at programming, but was really interested in this topic so I tried to understand how to solve that without using any libraries or scripts

I wrote this on scratchpad

var toAlgebraic = function(f1, f2) {
    let f1_base = Math.pow(10, f1.split('.')[1].length);
    let f2_base = Math.pow(10, f2.split('.')[1].length);
    f1 = parseInt(f1.replace('.', ''));
    f2 = parseInt(f2.replace('.', ''));

    let dif, base;
    if (f1_base > f2_base) {
        dif = f1_base / f2_base;
        base = f1_base;
        f2 = f2 * dif;
    } else {
        dif = f2_base / f1_base;
        base = f2_base;
        f1 = f1 * dif;
    }

    return (f1 * f2) / base;
};

console.log(0.1 * 0.2);
console.log(toAlgebraic("0.1", "0.2"));

you might need refactor this code, because I am not good at programming :)

查看更多
登录 后发表回答