浮動小数点数を使用して計算すると、困惑する結果が得られます。負の数を生成する負の数を生成してはならないコードがあるため、平方根を取得しようとすると NaN が発生します。
このコードは、テストでは非常にうまく機能するようです。ただし、実際の数値 (つまり、7 および 8 の負の指数が非常に小さい可能性がある) の数値を操作すると、合計は最終的に負になり、NaN につながります。理論的には、減算ステップは、既に に追加された数値のみを削除しsum
ます。これは浮動小数点エラーの問題ですか? それを修正する方法はありますか?
コード:
public static float[] getRmsFast(float[] data, int halfWindow) {
int n = data.length;
float[] result = new float[n];
float sum = 0.000000000f;
for (int i=0; i<2*halfWindow; i++) {
float d = data[i];
sum += d * d;
}
result[halfWindow] = calcRms(halfWindow, sum);
for (int i=halfWindow+1; i<n-halfWindow; i++) {
float oldValue = data[i-halfWindow-1];
float newValue = data[i+halfWindow-1];
sum -= (oldValue*oldValue);
sum += (newValue*newValue);
float rms = calcRms(halfWindow, sum);
result[i] = rms;
}
return result;
}
private static float calcRms(int halfWindow, float sum) {
return (float) Math.sqrt(sum / (2*halfWindow));
}
いくつかの背景について: 信号データのローリング 2 乗平均平方根 (RMS) 関数を計算する関数を最適化しようとしています。最適化は非常に重要です。それは私たちの処理におけるホットスポットです。基本的な方程式は単純です - http://en.wikipedia.org/wiki/Root_mean_square - ウィンドウ上のデータの二乗を合計し、その合計をウィンドウのサイズで割り、二乗します。
元のコード:
public static float[] getRms(float[] data, int halfWindow) {
int n = data.length;
float[] result = new float[n];
for (int i=halfWindow; i < n - halfWindow; i++) {
float sum = 0;
for (int j = -halfWindow; j < halfWindow; j++) {
sum += (data[i + j] * data[i + j]);
}
result[i] = calcRms(halfWindow, sum);
}
return result;
}
このコードは、ウィンドウのオーバーラップを利用するのではなく、各ステップで配列からウィンドウ全体を読み取るため、低速です。意図した最適化は、最も古い値を削除して最新の値を追加することにより、そのオーバーラップを使用することでした。
新しいバージョンの配列インデックスをかなり注意深くチェックしました。意図したとおりに機能しているように見えますが、その領域では間違いがある可能性があります。
更新:
私たちのデータでは、型をsum
double に変更するだけで十分でした。なぜそれが私に起こらなかったのかわかりません。しかし、私は否定的なチェックインを残しました。そして、FWIW、400 サンプルごとに合計を再計算することで優れた実行時間と十分な精度が得られる sol'n を実装することもできました。ありがとう。