javascript - 用JavaScript添加一堆 float ,总和的错误是什么?

标签 javascript floating-point

当我用JavaScript添加一堆浮点数时,总和上的错误是什么?应该使用什么错误界限来检查两个和是否相等?

在一个简单的脚本中,我添加了一堆浮点数并比较总和。我注意到有时结果不正确(两个应该相等的总和不正确)。我在数值分析方面相当虚弱,但是即使在回顾了Is floating point math broken?What Every Computer Scientist Should Know About Floating-Point ArithmeticComparing Floating Point Numbers, 2012 Edition之后,我仍然对如何最好地比较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。精确舍入运算的中间结果仍被舍入,这仍然可能导致错误。

机器epsilon有多大? 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 *机器epsilon *绝对值的和限制),但同时指出,如果该和以IEEE双精度格式进行,则机器epsilon为大约1e-16,因此对于任何合理的n值,n *机器epsilon都将远小于1(n远小于1e16)。如何使用此错误界限检查两个浮点和是否相等?如果总和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,它是以2为底的1/2 ULP?

如果两个浮点和相等,则它们的绝对差必须小于误差范围的两倍,以2为底的ULP为1?因此在JavaScript中,Math.abs(a-b)
Comparing Floating Point Numbers, 2012 Edition描述了另一种基于相对误差的比较浮点数的技术。在JavaScript中,是否可以找到两个浮点数之间可表示数字的数量?

最佳答案

连续相加的n个数字之和的最大可能误差与n2成正比,而不与n成正比。

JavaScript由ECMA Language Specification指定,该JavaScript表示使用IEEE-754 64位二进制浮点,并且使用“舍入至最近”模式。我看不到任何规定像某些语言一样允许额外的精度。

假设所有数字的大小最大为b,其中b是一些可表示的值。如果您的数字具有可以更精确地描述的分布,则可能会得出比以下描述更严格的错误。

当运算的确切数学结果是y,并且没有溢出时,则采用舍入至最近模式的IEEE-754二进制浮点数中的最大误差为1/2 ULP(y),其中ULP(y)是大小上恰好高于和低于y的两个可表示值之间的距离(如果y可以精确表示,则将y本身用作“上方”值)。这是最大的误差,因为y总是恰好在两个边界值之间的中点,或者在一侧或另一侧,所以从y到边界值之一的距离最多是从中点到边界值的距离。

(在IEEE-754 64位二进制文​​件中,所有小于2-1022的数字的ULP的大小为2-1074。所有2的幂的大数的ULP是数字的2到52倍;例如1的2到52 。非2的幂的ULP是小于该数字的2的最大幂的ULP,例如,对于大于1和小于2的任何数字,则为2-52。

当将系列中的前两个数字相加时,精确的结果最多为2b,因此第一次相加时的误差最多为1/2 ULP(2b)。当第三个数字相加时,结果最多为3b,因此此相加中的错误最多为1/2 ULP(3b)。到目前为止,总误差最多为1/2(ULP(2b)+ ULP(3b))。

在这一点上,加法可能会四舍五入,因此到目前为止,部分和可能略大于3b,而下一个和可能会略大于4b。如果要计算错误的严格界限,可以使用以下算法:

Let bound = 0.
For i = 2 to n:
    bound += 1/2 ULP(i*b + bound).


也就是说,对于将要执行的每个加法,在给定的实际值加上所有先前的误差的情况下,添加一个误差范围,该误差范围是最大可能结果的ULP的1/2。 (上面的伪代码需要扩展精度或向上舍入以保持数学上的严格性。)

因此,仅给出要添加的数字的数量及其大小的界限,我们就可以预先计算误差界限,而无需事先知道它们的具体值。该误差范围将与n2成正比。

如果此潜在错误过高,则可以通过以下方法来减少它:


不必将数字连续地相加,而是可以将它们分成两半,并且可以将两半的和相加。每个部分都可以这种方式递归求和。完成此操作后,部分和的最大量将较小,因此其误差的范围将较小。例如,连续相加1,我们得到的总和为2、3、4、5、6、7、8,但是通过这种拆分,我们得到的平行总和为2、2、2、2、4、4,然后8。
我们可以对数字进行排序,并通过添加相互抵消的数字(正负数字互补)或首先添加较小的数字来使总和较小。
可以使用Kahan summation algorithm来获得一些扩展的精度,而无需付出额外的努力。




考虑一种特殊情况:

考虑将n个非负数相加,得出计算出的总和s。则s中的误差最多为(n-1)/ 2•ULP(s)。

证明:每次加法最多具有1/2 ULP(x)的误差,其中x是计算值。由于我们要添加非负值,因此累加和永远不会减少,因此它永远不会超过s,并且其ULP最多是s的ULP。因此,n-1个加法最多产生ULP(s)/ 2的n-1个错误。

关于javascript - 用JavaScript添加一堆 float ,总和的错误是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19895461/

相关文章:

javascript - 函数 stub 不适用于 sinon 和 mocha

c - float 减法精度差异

java - 加减精确值以 float

javascript - React Native activeTintColor 未应用于选定的抽屉项目

javascript - 尝试使用 VueJS 插值从嵌套对象的属性检索值时出错

javascript - 计算没有 jQuery 的 HTML 文本区域中显示的行数(不是换行符)?

javascript - 使用 JavaScript 单击 HREF

c - 仅使用单精度 float 逼近 [0,pi] 上的余弦

php - 如何在 PHP 中解决这个问题?

types - 为什么存储在 Float 数据类型中的数据被视为近似值?