简体   繁体   English

检查十进制浮点数

[英]checking decimal floating point number

Normally we expect 0.1+0.2===0.3 to be true. 通常我们期望0.1+0.2===0.3为真。 But it is not what javascript will result. 但这不是javascript的结果。 As javascript displays decimal floating point number but stores binary floating point number internally. 由于javascript显示十进制浮点数,但在内部存储二进制浮点数。 So this returns false. 因此,这将返回false。

If we use chrome developer tool console, we'll get the following result: 如果我们使用chrome开发人员工具控制台,则会得到以下结果:

0.1+0.2;//0.30000000000000004

0.1+1-1;//0.10000000000000009


0.1 + 0.2 === 0.3 ;// returns false but we expect to be true.
0.1+1-1===0.1;//returns false

Due to rounding errors, as a best practice we should not compare non-integers directly. 由于舍入错误,作为最佳实践,我们不应直接比较非整数。 Instead, take an upper bound for rounding errors into consideration. 取而代之的是,将舍入误差的上限考虑在内。 Such an upper bound is called a machine epsilon. 这样的上限称为机器ε。

And here is the epsilon method: 这是epsilon方法:

var eps = Math.pow(2,-53);
function checkEq(x,y){
    return Math.abs(x - y) <  eps;
}

Now, if we check it returns true. 现在,如果我们检查它返回true。

checkEq(0.1+0.2,0.3);// returns true
checkEq(0.1+1-1,0.1);//returns true

It's okay and fine. 没关系,很好。 But if I check this: 但是如果我检查一下:

checkEq(0.3+0.6,0.9);// returns false

Which is not okay and not as what we expect. 这不行,也不符合我们的预期。

So, how should we do to return the correct results? 那么,我们应该如何返回正确的结果呢?


What I've tried to solve this is like this: 我试图解决的问题是这样的:

var lx,ly,lxi,lyi;
function isFloating(x){
   return x.toString().indexOf('.');
}
function checkEq(x,y){
   lxi = x.toString().length - x.toString().indexOf('.') - 1;
   lyi = y.toString().length - y.toString().indexOf('.') - 1;
   lx = isFloating(x) > -1 ? lxi : 0;
   ly = isFloating(y) > -1 ? lyi : 0;
   return x.toFixed(lx) - y.toFixed(ly)===0;
}

Now, fixed. 现在,修复。 And it results fine if I check like this: 如果我这样检查,结果很好:

checkEq(0.3,0.3); //returns true

But the following returns false 但是以下返回false

checkEq(0.3+0.6,0.9)

As here first it's value is stored in binaray floating point number and then returning decimal floating point number after calculating. 此处首先将其值存储在binaray浮点数中,然后在计算后返回十进制浮点数。

So now, how can I set toFixed() method for each input like in checkEq(0.3+0.6,0.9) 0.3.toFixed(lx) and 0.6.toFixed(lx) and then only add: 所以现在,我如何才能为每个输入设置toFixed()方法,例如在checkEq(0.3+0.6,0.9) 0.3.toFixed(lx) and 0.6.toFixed(lx) ,然后仅添加:

    var lx,ly,lxi,lyi;
    function isFloating(x){
    return x.toString().indexOf('.');
    }
    function checkEq(x,y){
    x = x.toString().split(/\+ | \- | \/ | \ | \\ */);
    y = x.toString().split(/\+ | \- | \/ | \ | \\*/);

    for(var i=0;i<x.length,y.length;i++){
    //here too I may be wrong...
       lxi = x[i].toString().length - x[i].toString().indexOf('.') - 1;
       lyi = y[i].toString().length - y[i].toString().indexOf('.') - 1;
    // particularly after this would wrong...
       lx = isFloating(x[i]) > -1 ? lxi : 0; 
       ly = isFloating(y[i]) > -1 ? lyi : 0;

    //And, here I'm stucked too badly...
    //take splitted operators to calculate:
    //Ex- '0.3 + 1 - 1' 
    // Number('0.3').toFixed(1) + Number('1').toFixed(0) - Number('1').toFixed(0)
    //But careful, we may not know how many input will be there....


    }

//return x.toFixed(lx) - y.toFixed(ly)===0;
}

Other answers are also welcome but helping me with my code is greatly appreciated. 也欢迎提供其他答案,但是非常感谢您帮助我编写代码。

Perhaps you should try out some existing JS Math library such as bignumber.js , which supports arbitrary-precision arithmetic. 也许您应该尝试一些现有的JS Math库,例如bignumber.js ,它支持任意精度算术。 Implementing everything from scratch will be rather time consuming and tedious. 从头开始实施所有工作将非常耗时且乏味。

Example

0.3+0.6    //0.8999999999999999
x = new BigNumber('0.3')    // "0.3"
y = new BigNumber('0.6')    // "0.6"
z = new BigNumber('0.9')    // "0.9"
z.equals(x.plus(y))    // true

I think you should take a little larger value for epsilon. 我认为您应该将epsilon的价值提高一些。

You can also have a look at math.js : the comparison functions of math.js also check for near equality. 您还可以看一下math.jsmath.js的比较函数还会检查是否相等。 Comparison is explained here: 比较说明如下:

http://mathjs.org/docs/datatypes/numbers.html#comparison http://mathjs.org/docs/datatypes/numbers.html#comparison

So you can do: 因此,您可以执行以下操作:

math.equal(0.1 + 0.2, 0.3); // true
math.equal(0.3 + 0.6, 0.9); // true

even better, math.js has support for bignumbers ( see docs ), so you can do: 更好的是,math.js支持bignumbers( 请参阅docs ),因此您可以执行以下操作:

math.equal(math.bignumber(0.1) + math.bignumber(0.2), math.bignumber(0.3);

or using the expression parser: 或使用表达式解析器:

math.config({number: 'bignumber'});

math.eval('0.1 + 0.2');        // returns BigNumber 0.3, not 0.30000000000000004
math.eval('0.1 + 0.2 == 0.3'); // returns true

Discontinuous functions such as equality (but also floor and ceil) are badly affected by rounding errors, and taking an epsilon into account may work in some cases, but may also give an incorrect answer (eg abs(xy) < eps may return true while the exact value of x and y are really different); 舍入错误会严重影响不连续的函数(例如相等性(也包括floor和ceil)),并且在某些情况下考虑到epsilon可能会起作用,但可能还会给出错误的答案(例如abs(xy) < eps可能在返回true时返回true x和y的确切值确实不同); you should do an error analysis to make sure that it is OK. 您应该进行错误分析以确保可以。 There is no general way to solve the problem in floating point: this depends on your application. 没有通用的方法可以解决浮点问题:这取决于您的应用程序。 If your inputs are decimal numbers and you just use addition, subtraction and multiplication, a decimal floating-point arithmetic may be OK if the precision is large enough so that all your data can be represented exactly. 如果您输入的是十进制数,而您仅使用加,减和乘,则十进制浮点算术可能是可以的,前提是精度足够大,以便可以正确表示所有数据。 You can also use a rational arithmetic, such as big-rational (not tried). 您还可以使用有理算术,例如大理性 (未尝试)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM