用运行平均值保持浮点精度

Jas*_*rge 13 c++ floating-point

我需要计算任意数量的数据点(超过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);
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来实现这一点来保持准确性?我考虑将MSE归一化为1并简单地在完成时保留最后一个除法的总和来计算平均值.

Pau*_*l R 13

你可能想看看Kahan的求和算法 -这不是正是你所需要的东西,但它解决了一个非常类似的问题,您可以将它适应您的需求.


Pot*_*ter 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;
}
Run Code Online (Sandbox Code Playgroud)