3

我正在尝试计算一组浮点数的平均值。我需要使用索引,因为这是在二进制搜索中,所以顶部和底部会移动。(大图我们正在尝试优化半范围估计,因此我们不必每次通过都重新创建数组)。

无论如何,我写了一个自定义平均循环,我得到的准确度比 c# Average() 方法低 2 个位置

float test = input.Average();

int count = (top - bottom) + 1;//number of elements in this iteration
int pos = bottom;
float average = 0f;//working average
while (pos <= top)
{
     average += input[pos];
     pos++;
}
average = average / count;

例子:

0.0371166766 - c#
0.03711666 - 我的循环

125090.148 - c#
125090.281 - 我的循环

http://pastebin.com/qRE3VrCt

4

3 回答 3

5

我得到的准确度比 c# Average() 低 2 个位置

不,您只会丢失 1 个有效数字。浮点型只能存储 7 位有效数字,其余只是随机噪声。在这样的计算中,不可避免地会累积舍入误差,从而失去精度。让舍入误差平衡需要运气。

避免它的唯一方法是使用具有更高精度的浮点类型来累积结果。不是问题,你有双倍可用。这就是为什么 Linq 平均方法看起来像这样的原因:

   public static float Average(this IEnumerable<float> source) {
       if (source == null) throw Error.ArgumentNull("source");
       double sum = 0;         // <=== NOTE: double
       long count = 0;
       checked {
           foreach (float v in source) {
               sum += v;
               count++;
           }
       }
       if (count > 0) return (float)(sum / count);
       throw Error.NoElements();
   }

使用double重现 Linq 结果,结果中的有效数字位数相当。

于 2013-02-12T18:57:14.037 回答
2

我将其重写为:

int count = (top - bottom) + 1;//number of elements in this iteration
double sum = 0;
for(int i = bottom; i <= top; i++)
{
     sum += input[i];
}
float average = (float)(sum/count);

这样您就可以使用高精度累加器,这有助于减少舍入误差。

顺便提一句。如果性能不是那么重要,您仍然可以使用 LINQ 计算数组切片的平均值:

input.Skip(bottom).Take(top - bottom + 1).Average()

我不完全确定这是否适合您的问题,但如果您需要计算许多子数组的平均值,创建一个持久和数组可能很有用,因此计算平均值只需两个表查找和一个除法。

于 2013-02-12T18:39:02.440 回答
2

只是为了增加对话,在使用浮点原语时要小心。

每个计算机科学家都应该知道的关于浮点运算的知识

内部浮点数存储未反映在显示值中的附加最低有效位(又名:保护位或保护位)。但是,在执行数学运算和相等检查时会使用它们。一个常见的结果是包含 0f 的变量并不总是零。在累积浮点值时,这也可能导致精度错误。

使用 Decimal 作为累加器:

  1. 不会因保护数字而出现舍入错误
  2. 是 128 位数据类型(不太可能超过累加器中的最大值)。

欲了解更多信息: C# 中的 Decimal、Float 和 Double 有什么区别?

于 2013-02-12T23:29:49.783 回答