数字6.35无法精确表示:
alert( 6.35.toFixed(20) ); // 6.34999999999999964473
但是为什么6.35 * 10 == 63.5
是正确的呢?
6.35不够精确,10是精确的,而63.5是精确的。我无法理解为什么(不精确的* 精确的)等于精确的。
数字6.35无法精确表示:
alert( 6.35.toFixed(20) ); // 6.34999999999999964473
但是为什么6.35 * 10 == 63.5
是正确的呢?
6.35不够精确,10是精确的,而63.5是精确的。我无法理解为什么(不精确的* 精确的)等于精确的。
635/100-1/2,814,749,767,106,560
,或者如果您愿意,可以使用 635/100-1 /(10 * 2 ^ 48)
。因此,准确的* 10
操作应该回答 635/10-1/(2 ^ 48)
。但是,这个数量不能表示为双精度浮点数(请参见下文)......因此,最后一个操作是不准确的。 635/10
)和它的前任 635/10-1/(2 ^ 47)
。一个有趣的精确连接情况:精确数量与两个可表示的双倍邻居相同的距离, 默认舍入模式是四舍五入,连接到偶数,因此FPU将选择具有偶数有效位数的double,即 635/10
。(1 to: 10000) count: [:x | (x/10.0) = (x/10) and: [(x/100.0) ~= (x/100)]].
我得到了 1600 个案例,其中 x/10 能够被准确地表示为双精度浮点数,而 x/100 则不能。
如果我选择这 1600 个案例,并验证舍入误差是否被消除:
((1 to: 10000) select: [:x | (x/10.0) = (x/10) and: [(x/100.0) ~= (x/100)]])
count: [:x | (x/100.0*10) = (x/10)]
我发现在1600个案例中,误差被抵消了,这是IEEE754算法的一个好特性。但这仍然是运气。
如果我尝试将其除以1000.0再乘以100,我会得到一个错误答案:
((1 to: 10000) select: [:x | (x/10.0) = (x/10) and: [(x/1000.0) ~= (x/1000)]])
allSatisfy: [:x | (x/1000.0*100) = (x/10)]
Javascript使用IEEE 754浮点标准,其中所有数字都无法准确表示。
数字表示为2的幂的倍数,包括负二次幂,分母不是2的幂的数字无法准确表示。
出于同样的原因,我们得到0.1 + 0.2 == 0.3 //false
。
这是二进制浮点数最著名的副作用,对于所有使用IEEE 754格式表示数字的语言(不仅仅是Javascript),这一点仍然成立。
因此,在处理小数值时,有些情况需要更加小心。
最常接受的做法是在进行比较时使用微小的舍入误差
。这个值被称为机器epsilon
,它是2 ^ 52
。
从ES6开始,Number.EPSILON
预定义了这个容差值。
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
6.35
和10
相乘会得到恰好63.5,即使6.35
并不完全等于6.35。无论你在这个答案中尝试做什么,这个答案都没有回答被问出的问题。 - Eric PostpischilNumber.EPSILON
是错误的。使用浮点结果计算时产生的误差从0到无穷大,也可以是NaN,因此没有单个容差可以使用。 - Eric Postpischil(a)
,我已经更新了我的答案,对于点(b)
,是的,你绝对是正确的,没有单一的公差可以适用。但是在这里,我试图比较两个浮点值,其中一个可以是操作的结果,另一个可以是期望的,例如0.1 + 0.3
将得到不同的结果,但期望的结果是0.3
,因此您可以使用numbersCloseEnoughToEqual
来获得正确的结果。 - Akshay Bande
6
...也许这个问题可以帮助您。 - Bravo