给出平均函数的两个实现:
float average(const vector<float>& seq)
{
float sum = 0.0f;
for (auto&& value : seq)
{
sum += value;
}
return sum / seq.size();
}
和
float average(const vector<float>& seq)
{
float avg = 0.0f;
for (auto&& value : seq)
{
avg += value / seq.size();
}
return avg;
}
为了说明我的问题,想象一下我们在输入数据方面存在巨大差异,如下所示:
1.0f, 0.0f, 0.0f, 0.0f, 1000000.0f
我的猜测是,在第一个实现中,sum
会增加“太多”并且松散最低有效数字,并且在和数循环结束时为1000000.0f
而不是1000001.0f
另一方面,第二个实现似乎理论上效率较低,因为要执行的分割数量(我没有描述任何内容,这是一个盲目猜测)。
那么,这些实现中的一个是否优于另一个?我是否认为第一次实施不太准确?
答案 0 :(得分:5)
我不会指望第二个更准确。该 元素大小的差异除以 向量的长度,但每个部门介绍一些 额外的不精确。
如果准确性有问题,则应使用第一个步骤
double
。即使向量是float
,出于内存原因,
函数内的计算应为double
。
除此之外,对于大量元素,你应该这样做 使用Kahan algorithm,而不是天真地添加 元素。虽然它在循环中添加了许多操作, 它会跟踪错误,并会显着地导致错误 更准确。
只是为了它的乐趣,我写了一个小程序,使用了 以下代码生成向量:
std::vector<float> v;
v.push_back( 10000000.0f );
for ( int count = 10000000; count > 0; -- count ) {
v.push_back( 0.1f );
}
平均值的结果应该是1.0999999(实际上是
说,1.1)。使用原始算法中的任何一种
发布,结果为0.999999881:误差为10%。只是
在第一个算法中将sum
更改为类型double
,
但是,结果1.0999999
,尽可能准确
得到。使用Kahan算法(随处浮动)给出
相同的结果。
答案 1 :(得分:0)
如果你的总和对于float
类型来说不是太大,那么第一个可能更准确,因为除法产生的个别舍入错误可能会累积