【问题标题】:Maintaining Floating Point Accuracy with a Running Average使用运行平均值保持浮点精度
【发布时间】:2011-01-27 19:07:20
【问题描述】:

我需要针对任意数量的数据点(超过 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,并在完成时简单地保留一个总和,最后除以计算平均值。

【问题讨论】:

  • 顺便说一句,根据您是否接受pow(double, int) 过载,iDifference*iDifference 可能比pow 调用快几个数量级。
  • 同意。我应该抓住它。谢谢马克!

标签: c++ floating-point


【解决方案1】:

您可能想查看Kahan Summation Algorithm - 这不是完全您需要的,但它解决了一个非常相似的问题,您可以根据您的需要调整它。

【讨论】:

  • 非常酷,+1。但是,如果运行总数足够大以至于添加任何错误都会有效地增加 0,这将不再有效。此时,错误累加器开始快速增长,直到它也失去精度。然后他又回到了 1 号广场。
  • +1,绝对酷。我倾向于同意 Potatoswatter 的观点,即随着数据点数量的增加,我仍然会降低精度。
【解决方案2】:

浮点数在这种情况下不会溢出,它们只会丢失精度。因此,这里的运行平均值与运行总数相比没有优势。无论累计还是分母增长,结果都是一样的。

要保持运行总计的准确性,请保留小计而不是单个总计。继续添加小计,直到再添加一个会导致溢出。然后继续下一个小计。由于它们都是相同的数量级(以 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;
}

【讨论】:

  • "浮点数在这种情况下不会溢出,只会丢失精度。"失去精度不会导致溢出吗?不太精确的浮点数可能小于大于实际值,可能不是?
  • @JosephGarvin 当加数的最高有效位小于总数的最低有效位时,保证向下舍入。否则,您是对的,四舍五入的目的是防止这种偏差。 (但是,完全消除偏差需要数字的线性分布,而许多数据集具有指数分布或其他不均匀分布。)设计良好的程序甚至不应该接近溢出(到无穷大);我认为 INFINITY 在误差范围内是一个错误。
【解决方案3】:

如果您的其他解决方案不起作用,您可以调查Bignum library

“GMP 是用于任意精度算术的免费库,对有符号整数、有理数和浮点数进行操作。除了运行 GMP 的机器中可用内存所暗示的精度外,对精度没有实际限制。 GMP功能丰富,功能接口规范。”

【讨论】:

  • 我也喜欢这个。最终决定我宁愿不为单个变量添加任何额外的开销。
猜你喜欢
  • 1970-01-01
  • 2021-06-25
  • 2012-06-09
  • 1970-01-01
  • 1970-01-01
  • 2013-05-26
  • 2017-01-19
  • 2020-09-24
相关资源
最近更新 更多