Handling comparisons of decimals in JS using "machine epsilon" as the tolerance of the comparison

Content from: Chapter 2 (Values) of Types and Grammars in You-dont-know-JS

decimal value

The most famous (and notorious) side effects of using binary floating point are (remember, this is true for all languages ​​that use IEEE 754 - not a problem that many people think/pretend to exist only in JavaScript):

0.1 + 0.2 === 0.3; // false

Mathematically, we know that this statement should be true. Why is it false?

Simply put, the0.1 binary representation of and is inexact, so when they are added, the result is not exact . It's a very close value: , but if your comparison fails, "closer" is irrelevant.0.20.30.30000000000000004

Note: Should JavaScript switch to a different numberimplementation ? Some people think it should. Many options have come up over the years. But none have been adopted, and probably never will. It looks as simple as waving your hand and saying, "That bug has been fixed!", but it's not at all. If it were that simple, it must have been changed a long time ago.

Now the question is, if some numbercan't be trusted to be exact, doesn't that mean we can't use it at numberall? Of course not .

In some applications you need to be careful, especially when dealing with decimals. There are many (maybe most?) applications that only deal with integers, and, at most, only a few million to a few trillion. These applications are, and always will be, very safe to use numeric manipulation in JS .

What if we really needed to compare two number, like 0.1 + 0.2AND 0.3, and knew that this simple equality test would fail?

The most common acceptable practice is to use a small "misrounding" value as the tolerance . This small value is often referred to as the "machine epsilon" , which numberis usually 2^-52(2.220446049250313e-16).

In ES6, using this tolerance value is predefined Number.EPSILON, so you'll want to use it, and you can safely fill this definition in pre-ES6 as well:

if (!Number.EPSILON) {
    Number.EPSILON = Math.pow(2,-52);
}

We can use this Number.EPSILONto compare numberthe "equivalence" of two (with tolerance for incorrect rounding):

function numbersCloseEnoughToEqual(n1,n2) {
    return Math.abs( n1 - n2 ) < Number.EPSILON;
}

var a = 0.1 + 0.2;
var b = 0.3;

numbersCloseEnoughToEqual( a, b );                  // true
numbersCloseEnoughToEqual( 0.0000001, 0.0000002 );  // false

The largest floating point value that can be represented is roughly 1.798e+308(it's really, really, really big!), which is predefined for you as Number.MAX_VALUE. On the extremely small end, Number.MIN_VALUEpresumably 5e-324, it's not negative but very close to 0!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324851824&siteId=291194637