我需要计算任意数量的数据点(超过1亿)的16位运算的均方误差。我决定使用平均值,所以我不必担心因添加大量平方错误而导致溢出。在1亿个样本中,我遇到了浮点精度问题(结果不准确),所以我加倍了。
这是我的代码
int iDifference = getIdeal() - getValue();
m_iCycles++;
// calculate the running MSE as
// http://en.wikipedia.org/wiki/Moving_average
// MSE(i + 1) = MSE(i) + (E^2 - MSE(i))/(i + 1)
m_dMSE = m_dMSE + ((pow((double)iDifference,2) - m_dMSE) / (double)m_iCycles);
有没有更好的方法来实现这一点以保持准确性?我考虑将MSE归一化为1,并简单地在完成时保留最终除法的总和来计算平均值。
答案 0 :(得分:13)
您可能希望查看Kahan Summation Algorithm - 它不是完全您需要的内容,但它解决了一个非常类似的问题,您可以根据自己的需要进行调整。< / p>
答案 1 :(得分:6)
浮点数在这种情况下不会溢出,它们只会失去精度。因此,在这里,运行平均值没有优势。无论运行总数还是分母增长,结果都是一样的。
要保持运行总计的精确度,请保留小计而不是单个总计。只需继续添加小计,直到再添加一个会导致溢出。然后转到下一个小计。由于它们都是相同的数量级(在基数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;
}
答案 2 :(得分:2)
如果您的其他解决方案不起作用,您可以调查Bignum library
“GMP是一个用于任意精度算术的免费库,对有符号整数,有理数和浮点数进行操作。除了机器GMP中可用内存所隐含的精度之外,精度没有实际限制。 GMP具有丰富的功能,并且功能具有常规接口。“