Числа с плавающей точкой не переполняются в такой ситуации, они только теряют точность.Таким образом, здесь нет преимуществ скользящего среднего по сравнению с промежуточным.Следствием является то же самое, растет ли промежуточный итог или знаменатель.
Чтобы сохранить точность в промежуточном итоге, сохраните промежуточные итоги вместо единого итога.Просто продолжайте добавлять к промежуточному итогу, пока добавление еще одного не вызовет переполнение.Затем перейдите к следующему промежуточному итогу.Поскольку все они имеют одинаковый порядок величины (в базе 2), оптимальная точность может быть достигнута путем преобразования в плавающую точку и использования попарного накопления в одну итоговую сумму.
// first = errors, second = counter
typedef pair< vector< uint32_t >, uint32_t > running_subtotals;
void accumulate_error( uint32_t error, running_subtotals &acc ) {
( numeric_limits< uint32_t >::max() - error < acc.first.back()?
* acc.first.insert( acc.first.end(), 0 ) : acc.first.back() )
+= error; // add error to current subtotal, or new one if needed
++ acc.second; // increment counter
}
double get_average_error( running_subtotals const &total ) {
vector< double > acc( total.first.begin(), total.first.end() );
while ( acc.size() != 1 ) {
if ( acc.size() % 2 ) acc.push_back( 0 );
for ( size_t index = 0; index < acc.size() / 2; ++ index ) {
acc[ index ] = acc[ index * 2 ] + acc[ index * 2 + 1 ];
}
acc.resize( acc.size() / 2 );
}
return acc.front() / total.second;
}