-1

鉴于平均函数的这两种实现:

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在求和循环的末尾。

另一方面,第二种实现在理论上似乎效率较低,因为要执行的部门数量较多(我没有分析任何内容,这是一个盲目的猜测)。

那么,这些实现中的一种是否比另一种更可取?我真的认为第一个实现不太准确吗?

4

2 回答 2

5

我不会指望第二个更准确。元素大小的差异除以向量的长度,但每个除法都会引入一些额外的不精确性。

如果准确性是一个问题,第一步应该是使用 double. 即使向量是float,出于内存原因,函数内的计算也应该是double

除此之外,对于大量元素,您可能应该使用Kahan 算法,而不是简单地添加元素。尽管它在循环中添加了许多操作,但它会跟踪错误,并将显着提高准确性。

编辑:

只是为了好玩,我写了一个小程序,它使用以下代码生成向量:

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为有类型,就会得到尽可能准确的结果。使用 Kahan 算法(到处都是浮点数)给出了相同的结果。double1.0999999

于 2013-05-03T08:23:12.733 回答
0

如果您的总和对于 type 来说不是太大float,那么第一个可能更准确,因为除法产生的单个舍入误差可能会累积

于 2013-05-03T08:15:21.440 回答