Reputation: 727
Number 6.35 can't be accurately represented:
alert( 6.35.toFixed(20) ); // 6.34999999999999964473
But why 6.35 * 10 == 63.5
is true?
6.35 is not accurate, 10 is accurate, and 63.5 is accurate. I can't understand how (inaccurate * accurate) equal accurate.
Upvotes: 3
Views: 241
Reputation: 9382
This is an error of logic: the last operation 6.35 * 10.0 is innacurate rather than accurate.
It's just that it might happen that several consecutive "rounding error" annihilate, as it might also happen that they cumulate.
The nearest double to 635/100 is 635/100 - 1/2,814,749,767,106,560
Or if you prefer: 635/100 - 1/(10 * 2^48)
So an accurate *10
operation should answer 635/10 - 1/(2^48)
.
But this quantity is not representable as double precision (see below)...
So the last operation is innacurate.
The two neighbours are 63.5 (which is exactly 635/10
) and its predecessor 635/10 - 1/(2^47)
.
An interesting case of exact tie: the exact quantity is at same distance of the two representable double neighbours, The default rounding mode is round to nearest, tie to even, so the FPU will choose the double with even significand, that is 635/10
.
Is this luck or is it a nice property of IEEE 754 arithmetic?
If I evaluate this snippet in Squeak/Pharo Smalltalk (which have exact fraction and comparison of exact arithmetic values):
(1 to: 10000) count: [:x | (x/10.0) = (x/10) and: [(x/100.0) ~= (x/100)]].
I get 1600 cases where the x/10 is exactly representable as double, while x/100 is not.
If I select those 1600 cases, and verify if rounding error annihilates:
((1 to: 10000) select: [:x | (x/10.0) = (x/10) and: [(x/100.0) ~= (x/100)]])
count: [:x | (x/100.0*10) = (x/10)]
I count 1600 cases out of the 1600 for which the error annihilate, so it is a nice property of IEEE754 arithmetic. But this is still luck.
If I retry by dividing by 1000.0 then multiply by 100, I get a false answer to this question:
((1 to: 10000) select: [:x | (x/10.0) = (x/10) and: [(x/1000.0) ~= (x/1000)]])
allSatisfy: [:x | (x/1000.0*100) = (x/10)]
The answer is true for 1649 cases out of the 1920, which is already a good score.
Upvotes: 3
Reputation: 2587
Javascript uses IEEE 754 floating-point standard in which all the numbers can't be represented accurately.
Numbers are represented as multiples of powers of 2, including negative powers of two, the numbers whose denominator is not the power of 2 cannot be represented accurately.
For the same reason, we get 0.1 + 0.2 == 0.3 //false
.
This is the most famous side effect of binary floating-point numbers which remains true for all the languages which use IEEE 754 format to represent numbers (not just Javascript).
Hence there are some cases in which you need to be more careful, especially when dealing with fractional decimal values.
The most commonly accepted practice is to use a tiny rounding error
while making comparisons. This value is known as machine epsilon
which is 2 ^ 52
.
As of ES6, Number.EPSILON
is predefined with this tolerance value.
function numbersCloseEnoughToEqual(n1,n2) {
return Math.abs( n1 - n2 ) < Number.EPSILON;
}
var a = 6.35.toFixed(20);
var b = 6.35;
console.log(numbersCloseEnoughToEqual( a, b ));// true
Upvotes: 0