[英]Fast calculation of RMS gives NaNs in Java - floating point error?
使用浮點數進行數學運算時出現令人困惑的結果。 我的代碼永遠不會產生負數,而產生負數,這在嘗試取平方根時會導致NaN。
這段代碼在測試中看起來效果很好。 但是,當在現實世界中運行時(即可能很小,有七個和八個負指數),最終總和將變為負數,從而導致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));
}
對於某些背景:我正在嘗試優化一個函數,該函數可計算信號數據的滾動均方根(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
的類型更改為雙sum
就足夠了。 不知道為什么我沒想到。 但是我保留了否定檢查功能。在FWIW中,我還能夠實現sol'n,其中每400個樣本重新計算總和可以提供很好的運行時間和足夠的准確性。 謝謝。
這是浮點錯誤問題嗎?
是的。 由於四舍五入,您在減去先前的求和后很可能得到負值。
例如:
float sum = 0f;
sum += 1e10;
sum += 1e-10;
sum -= 1e10;
sum -= 1e-10;
System.out.println(sum);
在我的機器上,此打印
-1.0E-10
即使在數學上,結果也恰好為零。
這就是浮點的性質: 1e10f + 1e-10f
給出與1e10f
完全相同的值。
就緩解策略而言:
double
而不是float
來提高精度。 嘗試在第二個循環中檢查索引。 i
的最后一個值是n-halfWindow-1
而n-halfWindow-1+halfWindow-1
是n-2
。
您可能需要將循環更改為for (int i=halfWindow+1; i<n-halfWindow+1; i++)
。
您會遇到浮點數問題,因為您認為它們就像數學實數一樣。 它們不是,而是實數的近似值,映射為離散數,並在混合中添加了一些特殊規則。
如果您打算經常使用它們,請花一些時間閱讀每個程序員應該了解的浮點數 。 如果不加注意,浮點數和實數之間的差異可能會以最差的方式出現並咬住您。
或者,只要相信我的話,就知道每個浮點數都“非常接近”所要求的值,有些浮點數“精確”,但大多數“大部分”准確。 這意味着您需要考慮測量誤差,並在計算之后牢記在心,否則可能會認為在計算值的末尾有准確的結果(不是)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.