浮点值的数值稳定运行平均值

use*_*931 2 floating-point numerical-methods

使用 32 位浮点值,如果 - 在开始计算时 - 我不知道我将拥有多少个值(在以下示例中,我只是遍历一个向量,那么计算平均值的最佳(数字最准确)方法是什么)我会知道 coult,但让我们假设我最后只知道元素计数)?

我可以做例如

float result = 0.f;
for(float num: numbers) {
    result += num;
}
num /= numbers.size();
Run Code Online (Sandbox Code Playgroud)

但随着结果变大,精度也会变大。对于较小的值,在某些时候result += num;实际上不会再改变结果。

我可以

float result = numbers[0]
for(int i=1, i<numbers.size(); i++) {
    float frac = (i/float(i+1));
    result = result * frac + numbers[i] * (1.0f-frac);
}
Run Code Online (Sandbox Code Playgroud)

但似乎我会应用累积错误来产生这种结果。

有没有更好的方法而不去 64bit double?

ali*_*ias 5

此类问题最著名的方法是 Kahan 求和法。请参阅此处:https : //en.wikipedia.org/wiki/Kahan_summation_algorithm。假设总和仍然可以表示为单精度浮点数,请在最后进行直接除法以找到平均值。

另请参阅此答案以进行一些额外的讨论,它或多或少地要求相同:如何计算双打的平均值,以便总误差最小?

  • 这是一种已知的方法,而不是最知名的方法。对 IEEE-754 二进制 64 数求和的最佳数值方法是维护一个 2098 位的数组,并将每个值添加到其中的适当位置。这没有错误,因为二进制 64 格式仅跨越 2098 位(2\*\*1023 到 2\*\*-1074)。提高性能的最佳方法是不执行任何操作、不维护数据并返回零。一切都是一个权衡。 (3认同)
  • @alias以下内容“足够好”,无需明确形成总和(我同意这对于OP指定的几千个输入来说是最好的):`mean = 0.0f; for (int i = 0; i &lt; N; i++) { 平均值 = fmaf (num[i] - 平均值, 1.0f/(i+1), 平均值); }`。从 20 世纪 60 年代开始就有文献介绍如何在单次数字声音中计算平均值、标准差和方差。最近的一篇论文:J. Bennett、R. Grout、P. Pébay、D. Roe、D. Thompson:“数值稳定、单通道、并行统计算法”。*2009 年 IEEE 国际集群计算会议和研讨会*,第 1-8 页 (3认同)