Javascript 浮点相等测试的混淆?

Javascript 浮点相等测试的混淆?,javascript,floating-point,precision,Javascript,Floating Point,Precision,数字6.35无法准确表示: 警报(6.35.toFixed(20));//6.34999999999999964473 但是为什么6.35*10==63.5是真的 6.35不准确,10准确,63.5准确。我不明白(不准确的*准确的)怎么会同样准确 Javascript使用标准,其中所有数字都无法准确表示 数字表示为2的幂的倍数,包括2的负幂,分母不是2的幂的数字无法准确表示 出于同样的原因,我们得到0.1+0.2==0.3//false 这是二进制浮点数最著名的副作用,对于所有使用IEEE75

数字6.35无法准确表示:

警报(6.35.toFixed(20));//6.34999999999999964473
但是为什么
6.35*10==63.5
是真的

6.35不准确,10准确,63.5准确。我不明白(不准确的*准确的)怎么会同样准确

Javascript使用标准,其中所有数字都无法准确表示

数字表示为2的幂的倍数,包括2的负幂,分母不是2的幂的数字无法准确表示

出于同样的原因,我们得到
0.1+0.2==0.3//false

这是二进制浮点数最著名的副作用,对于所有使用IEEE754格式表示数字的语言(不仅仅是Javascript)来说都是如此

因此,在某些情况下,您需要更加小心,尤其是在处理分数十进制值时

最普遍接受的做法是在进行比较时使用微小的
舍入误差。该值称为机器ε,即
2^52

从ES6开始,
Number.EPSILON
是使用此公差值预定义的

函数号接近相等(n1,n2){
返回Math.abs(n1-n2)console.log(number closeenoughtoequal(a,b));//true
这是一个逻辑错误:最后一个操作6.35*10.0不准确而不准确。
只是可能会发生几个连续的“舍入误差”湮灭,因为它们也可能累积起来

距离635/100最近的双精度是
635/100-1/2814749767106560

或者如果您愿意:
635/100-1/(10*2^48)

因此,准确的
*10
操作应该回答
635/10-1/(2^48)

但该数量不能表示为双精度(见下文)…
所以最后一次手术不准确

两个相邻的是63.5(确切地说是
635/10
),它的前身是
635/10-1/(2^47)

精确tie的一个有趣的例子:精确数量位于两个可表示的双近邻的相同距离处,默认舍入模式为舍入到最近,tie到偶数,因此FPU将选择有效位为偶数的双精度,即
635/10

这是运气还是IEEE 754算法的一个好特性?
如果我在Squeak/Pharo Smalltalk中评估这个片段(它有精确的分数和精确算术值的比较):

我得到了1600个例子,其中x/10可以表示为double,而x/100不能表示为double

如果我选择这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个错误消除的案例中的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)]

1920年中有1649个案例的答案是正确的,这已经是一个很好的分数。

javascript数字的精度为16或17位-您要求的是21位(包括
6
…可能会有帮助舍入系统的设计是为了最大限度地减少舍入误差,而不是保证它总是会影响比较。您的结果只是意味着10倍最接近6.35的数字,舍入到最接近的数字,是63.5。这并没有回答所问的问题。我试图解释浮动是如何产生的-由于IEEE 754标准
(Number.EPSILON)
,点数起作用,javascript必须尽可能减少副作用。我试图提到用户在处理小数点数值时应该做些什么来避免副作用。这就是用户问我不明白怎么做的意图(不准确*准确)同样准确。当然,您试图解释浮点数是如何工作的,以及JavaScript如何最大限度地减少浮点数运算的不良影响。但是,这并没有回答将
6.35
10
相乘如何精确地生成63.5的问题,即使
6.35
不精确地为6.35。不管w你在这个答案中试图做的是,这个答案并没有回答被问到的问题。此外:(a)“数字表示为整数的2次幂”是不正确的。这可能主要是一个语言问题;“数字表示为2次幂的倍数,包括2的负幂”是正确的。(b)建议与公差进行比较通常是不好的建议。(c)建议公差为绝对值
数字。ε
是错误的。使用浮点结果计算的结果中出现的错误从0到无穷大不等,也可能是NaN,因此不能使用单个公差。对于点
(a)
我已经更新了我的答案,关于
(b)点
是的,你肯定是正确的,没有一个公差可以使用。但我在这里尝试比较了两个浮动值,一个是操作的结果,另一个是预期的结果,例如
0.1+0.3
将是不同的结果,但预期值为0.3`,这样你就可以使用
numbersC获得正确的结果loseEnoughToEqual
((1 to: 10000) select: [:x | (x/10.0) = (x/10) and: [(x/1000.0) ~= (x/1000)]])
    allSatisfy: [:x | (x/1000.0*100) = (x/10)]