Any number encoding system occupying a finite number of bits, of whatever base you choose (e.g., decimal or binary), will necessarily be unable to represent all numbers exactly, because you are trying to represent an infinite number of points on the number line using a finite amount of memory. For example, a base-10 (decimal) system cannot represent 1/3 exactly, and a base-2 (binary) system cannot represent 0.1
exactly. Thus, for example, 0.1 + 0.2
is not exactly equal to 0.3
:
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
For this reason, it is often advised that floating point numbers should never be compared with ===
. Instead, we can deem two numbers as equal if they are close enough to each other. The Number.EPSILON
constant is usually a reasonable threshold for errors if the arithmetic is around the magnitude of 1
, because EPSILON
, in essence, specifies how accurate the number "1" is.
function equal(x, y) {
return Math.abs(x - y) < Number.EPSILON;
}
const x = 0.2;
const y = 0.3;
const z = 0.1;
console.log(equal(x + z, y)); // true
However, Number.EPSILON
is inappropriate for any arithmetic operating on a larger magnitude. If your data is on the 103 order of magnitude, the decimal part will have a much smaller accuracy than Number.EPSILON
:
function equal(x, y) {
return Math.abs(x - y) < Number.EPSILON;
}
const x = 1000.1;
const y = 1000.2;
const z = 2000.3;
console.log(x + y); // 2000.3000000000002; error of 10^-13 instead of 10^-16
console.log(equal(x + y, z)); // false
In this case, a larger tolerance is required. As the numbers compared have a magnitude of approximately 2000
, a multiplier such as 2000 * Number.EPSILON
creates enough tolerance for this instance.
function equal(x, y, tolerance = Number.EPSILON) {
return Math.abs(x - y) < tolerance;
}
const x = 1000.1;
const y = 1000.2;
const z = 2000.3;
console.log(equal(x + y, z, 2000 * Number.EPSILON)); // true
In addition to magnitude, it is important to consider the accuracy of your input. For example, if the numbers are collected from a form input and the input value can only be adjusted by steps of 0.1
(i.e., <input type="number" step="0.1">
), it usually makes sense to allow a much larger tolerance, such as 0.01
, since the data only has a precision of 0.1
.
Note: Important takeaway: do not simply use Number.EPSILON
as a threshold for equality testing. Use a threshold that is appropriate for the magnitude and accuracy of the numbers you are comparing.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4