javascript - 对浮点相等性测试感到困惑?
问题描述
数字 6.35 无法准确表示:
alert( 6.35.toFixed(20) ); // 6.34999999999999964473
但为什么6.35 * 10 == 63.5
是真的?
6.35 不准确,10 准确,63.5 准确。我不明白(不准确*准确)如何等于准确。
解决方案
这是一个逻辑错误:最后的运算 6.35 * 10.0 是不准确的而不是准确的。
只是可能会发生几个连续的“舍入误差”消失,因为它们也可能会累积。
最接近 635/100 的双倍是635/100 - 1/2,814,749,767,106,560
或者,如果您愿意:635/100 - 1/(10 * 2^48)
所以准确的*10
操作应该回答635/10 - 1/(2^48)
。
但是这个数量不能表示为双精度(见下文)......
所以最后一个操作是不准确的。
这两个邻居是 63.5(正是635/10
)和它的前身635/10 - 1/(2^47)
。
一个有趣的精确平局案例:精确数量与两个可表示的双邻域的距离相同,默认舍入模式是四舍五入到最近,平到偶数,因此 FPU 将选择具有偶数有效位的双精度数,即635/10
。
这是运气还是 IEEE 754 算法的一个很好的属性?
如果我在 Squeak/Pharo Smalltalk 中评估这个片段(它具有精确的分数和精确算术值的比较):
(1 to: 10000) count: [:x | (x/10.0) = (x/10) and: [(x/100.0) ~= (x/100)]].
我得到了 1600 个案例,其中 x/10 完全可以表示为 double,而 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 个错误消除的案例中的 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 例的答案是正确的,这已经是一个不错的分数了。
推荐阅读
- python-3.x - 在 keras 中获取权重
- text - 如何在 LaTeX 的表格下插入箭头?
- json - Python - 从字典对象将 json 写入 BytesIO 并存储为压缩的 tar.gz
- amazon-web-services - AWS Multicontainer Beanstalk 中是否可以有多个任务定义?
- c++ - 使用联合时未定义行为的情况
- python - 如何在 Python 和 plotly 中显示比我实际需要的更大的颜色条/色阶?
- python - 在 pandas 中解析一列字符串化字典
- java - 从 java 中的 csv 列添加:如何重置变量总数?
- javascript - Firebase 函数 - 使用具有不同参数的函数两次
- java - JsonFactory 未找到 Java 类异常