2011-01-27 6 views
12

任意の数のデータポイント(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に正規化し、平均を計算するために完了時の最終的な除算との合計を単に維持することを検討した。

+1

は、 'iDifference * iDifference'は速く' pow'コールよりも桁違いである可能性があります。 –

+0

合意。私はそれを捕らえておくべきだった。感謝の印! –

答えて

4

浮動小数点数は、このような状況ではオーバーフローしませんが、精度が低下します。したがって、稼働中の平均以上の利点はありません。結果は、実行中の合計か分母のどちらが大きくなっても同じです。

実行中の合計の精度を維持するには、1つの合計ではなく小計を保持します。小計を追加すると、オーバーフローが発生します。次に、次の小計に移動します。それらはすべて同じ大きさの桁数(基数2)であるため、浮動小数点に変換し、ペアごとの累積を1つの最終的な合計にすることによって最適精度を達成することができます。あなたは `POW(ダブル、int型)`過負荷を拾うかどうかに応じて、完全にサイドノートとして

// 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; 
} 
+0

解決策は素晴らしいです。 –

+0

"浮動小数点数はこの種の状況ではオーバーフローしませんが、精度を失うだけです。"精密なリードのオーバーフローを失うことはできませんか?それほど正確ではない浮動小数点は、実際の値よりも小さくても*大きくてもかまいません。 –

+0

@JosephGarvin加数の最上位ビットが合計の最下位ビットよりも小さい場合、切り捨てが保証されます。さもなければ、あなたはそうです。最も近いラウンドのポイントは、そのような偏見を防ぐことです。(バイアスを完全になくすには数値の線形分布が必要ですが、多くのデータセットは指数関数的に分布するのではなく、むしろ不均一な分布をしています。)うまく設計されたプログラムは決してオーバーフローすることはありません。私はエラーの余白の中に '無限大 'を持つバグだと思う。 – Potatoswatter

12

Kahan Summation Algorithmを参照してください。正確にここに必要なものはありませんが、非常によく似た問題が解決され、必要に応じて調整できます。

+0

非常に冷たい、+1。しかし、実行中の合計が十分大きいので、誤差を追加すると効果的に0が加算されます。その時点で誤差アキュムレータは精度を失うまで急速に増加し始めます。その後、彼は正方形1に戻ります。 – Potatoswatter

+0

+1、間違いなくクールです。 Potatoswatterには、データポイントの数が増えるにつれて精度が緩やかになるということに同意する傾向があります。 –

2

あなたの他のソリューションは、あなたがGMPは符号付き整数、有理数、および浮動小数点数で動作する、任意精度演算のための無料のライブラリである」Bignum library

を調査するかもしれない動作しない場合。実質的な制限はありませんGMPは豊富な関数セットを持っており、関数は通常のインターフェースを持っています。 "

+0

私もこれが好きです。最終的には、単一の変数に余分なオーバーヘッドを追加しないことにしました。 –

-1

あなたは指数関数的な移動平均のようです。これにより、後のデータポイントエラーの重み付けが以前のものより大きくなります。あなたが必要とするのは線形平均です。あなたのデータを平均して100万のブロックで平均し、それらのブロックの平均を取る。これを複数のレベルで行うこともできます。これは、すべての誤差点を等しく重み付けします。

関連する問題