在计算部分谐波序列时累积的计算误差

时间:2019-06-06 12:13:20

标签: c floating-point precision

我目前正在使用两种方法来计算谐波序列的部分和:

  1. 强行强迫(迭代)
  2. 使用某些提供解决方案捷径的公式

困扰我的问题是:double精确到小数点后15位,long double精确到小数点后19位。我正在处理从5到大约50的部分和。例如,要达到5的总和,您需要大约83个谐波系列的成员,而要达到50的总和,您需要大约2911068851283175669760的成员。谐波系列。我目前正在使用double。

在计算较大的总和(例如> 20)时,使用迭代和公式计算此类总和是否会失去准确性?

如果是这样,使用一些高精度库是否有好处?

1 个答案:

答案 0 :(得分:1)

使用任何可以为(无界)大整数和分数提供精确算术的语言,我们可以尝试使用非常幼稚的代码轻松评估舍入误差,至少对于小n而言(精确算术可能会变得令人望而却步)

我将在此处任意选择Squeak Smalltalk进行说明。

首先,让我们定义一个闭包,该闭包将返回确切的部分和

hn := [:n | (1 to: n) detectSum: [:i | i reciprocal]].

然后另一个将以较小的项开始的迭代返回Float(双精度):

fn := [:n | (n to: 1 by: -1) detectSum: [:i | i asFloat reciprocal]].

现在,另一个闭包将根据ulp来估计错误:

un := [:n | | f | ((f := fn value: n) asFraction - (hn value: n)) abs / f ulp].

现在,让我们搜索前1000个部分和的最坏情况:

i := (1 to: 1000) detectMax: [:n | un value: n ].
{i. un value: i}.

结果为#(807 6.101840327204507),H807的错误约为6 ulp。您所看到的并不多。

如果我们首先尝试对更大的谐波求和:

fn := [:n | (1 to: n) detectSum: [:i | i asFloat reciprocal]].

然后结果是#(471 5.714808765246509),5.7 ulp,少感到惊讶。

我们可以尝试使用后面的fn来进一步了解:

un value: 10000.
-> 1.8242087614993667

使用记忆,我发现100,000个第一学期(H〜7)的最大误差约为54 ulp,如果从较小的术语开始更仔细地求和,则误差约为26 ulp。

因此,多达数千个术语,没有用较小的术语开始求和(如果要扫描增加,则启用记忆),也不需要使用Kahan求和,除非您真的很在意最后几个ulp。

Kahan将成为数百万至数十亿个术语的有趣对象,但是对于较大的和,如果您近似欧拉·马斯切罗尼,公式将变得更快,更准确,自然对数永远不会超过几个。但是您应该显示代码,否则这仅仅是猜测。

例如,这种幼稚的二阶公式

fn := [:n | n ln + (0.5772156649015328606 + ((n*2.0) reciprocal - (n squared*12.0) reciprocal))].

对于较小的n值,将给出大约5.5 ulp的误差,但是对于MacOs libm良好的100,000,其误差小于100,000的1.5 ulp。