当我用 JavaScript 添加一堆浮点数时,总和的错误界限是什么?应该使用什么误差界限来检查两个和是否相等?
在一个简单的脚本中,我添加了一堆浮点数并比较总和。我注意到有时结果不正确(两个应该相等的总和不正确)。我在数值分析方面相当薄弱,但即使在复习之后浮点数学被破坏了吗?和What Every Computer Scientist Should Know About Floating-Point Arithmetic and Comparing Floating Point Numbers,2012 版我对如何最好地比较 JavaScript 中的浮点和感到困惑。
首先,我很困惑:IEEE 标准要求加法、减法、乘法和除法的结果必须精确四舍五入(就好像它们被精确计算然后四舍五入到最接近的浮点数一样)。如果 JavaScript 基于 IEEE 标准,0.1 + 0.2 != 0.3 怎么可能?
我想我自己回答了这个问题:我更容易考虑以 10 为底的示例。如果 1/3 近似为 0.333...333,2/3 近似为 0.666...667,则 1/3 + 1/ 3 = 0.666...666 完全四舍五入(它是两个近似值的精确总和)但 != 0.666...667。精确舍入运算的中间结果仍然是舍入的,这仍然会引入错误。
机器ε有多大?JavaScript 浮点数显然是 64 位的,显然 IEEE 双精度格式机器 epsilon 大约是 1e-16?
当我添加一堆 (n) 浮点数(朴素求和,没有成对或 Kahan 求和)时,总和的误差范围是多少?直观地说,它与 n 成正比。我能想到的最坏情况示例(再次以 10 为底)是 2/3 - 1/3 - 1/3 + 2/3 - 1/3 - 1/3 + 等。我认为每次迭代都会增加错误1 个 ULP 的项,而总和仍然为零,所以误差项和相对误差都会无限制地增长?
在“求和中的错误”部分中,Goldberg 更精确(错误项由 n * machine epsilon * sum of the absolute values)界定,但也指出如果求和以 IEEE 双精度格式进行,则机器 epsilon 为大约为 1e-16,因此对于任何合理的 n 值(n 远小于 1e16),n * machine epsilon 将远小于 1。这个错误界限如何用于检查两个浮点和是否相等?如果它们相等,那么和 1、1e-16、n 等之间的什么关系必须为真?
另一种直觉:如果一堆数字都是正数(我的都是正数),那么尽管误差项可以无限增长,但相对误差不会,因为总和必须同时增长。在以 10 为基数的情况下,我能想到的最坏情况示例(其中误差项增长最快而总和增长最慢)是 1.000...005 是否近似为 1.000...000。重复添加此数字将使误差项增加 1/2 ULP(被加数的 0.000...005),同时将总和增加 1 个第一位单位。最差的相对误差是 4.5 ULP(0.000...045,当总和为 9.000...000),即 (base - 1) / 2 ULP,即 1/2 ULP in base 2?
如果两个浮点和相等,那么它们的绝对差必须小于误差界限的两倍,即 1 ULP in base 2?所以在 JavaScript 中,Math.abs(a - b) < a * 1e-16 + b * 1e-16?
Comparing Floating Point Numbers, 2012 Edition描述了另一种比较浮点数的技术,同样基于相对误差。在 JavaScript 中,是否可以找到两个浮点数之间的可表示数字的数量?