c#float []平均失去准确性

时间:2013-02-12 18:31:32

标签: c# .net average

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

无论如何,我写了一个自定义平均循环,我得到的精度比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 - my loop

125090.148 - c#
125090.281 - my loop 

http://pastebin.com/qRE3VrCt

3 个答案:

答案 0 :(得分:3)

  

我的准确度低于c#Average()

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

避免它的唯一方法是使用具有更高精度的浮点类型来累积结果。不是问题,您可以使用 double 。这就是为什么Linq Average方法看起来像这样:

   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结果,并在结果中使用相同数量的有效数字。

答案 1 :(得分: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()

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

答案 2 :(得分:1)

只是要添加到对话中,使用浮点基元时要小心。

What Every Computer Scientist Should Know About Floating-Point Arithmetic

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

对累加器使用十进制:

  1. 由于Guard Digits不会出现舍入错误
  2. 是128位数据类型(不太可能超过累加器中的最大值)。
  3. 欲了解更多信息: What is the difference between Decimal, Float and Double in C#?