我正在尝试计算一系列浮点数的平均值。我需要使用索引,因为这是在二进制搜索中,所以顶部和底部将移动。 (大图我们正在尝试优化半范围估计,因此我们不必每次都重新创建数组。)
无论如何,我写了一个自定义平均循环,我得到的精度比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
答案 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的变量并不总是零。累积浮点值时,这也会导致精度误差。
对累加器使用十进制:
欲了解更多信息: What is the difference between Decimal, Float and Double in C#?