簡體   English   中英

RMS的快速計算為Java提供了NaN-浮點錯誤?

[英]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完全相同的值。

就緩解策略而言:

  1. 您可以使用double而不是float來提高精度。
  2. 有時,您可以完全重新計算平方和以減少舍入誤差的影響。
  3. 當總和變為負數時,您可以像上面的(2)一樣進行完全重新計算,或者將總和設置為零。 后者是安全的,因為您知道您將把總和推向真正的價值,並且永不背離它。

嘗試在第二個循環中檢查索引。 i的最后一個值是n-halfWindow-1n-halfWindow-1+halfWindow-1n-2

您可能需要將循環更改為for (int i=halfWindow+1; i<n-halfWindow+1; i++)

您會遇到浮點數問題,因為您認為它們就像數學實數一樣。 它們不是,而是實數的近似值,映射為離散數,並在混合中添加了一些特殊規則。

如果您打算經常使用它們,請花一些時間閱讀每個程序員應該了解的浮點數 如果不加注意,浮點數和實數之間的差異可能會以最差的方式出現並咬住您。

或者,只要相信我的話,就知道每個浮點數都“非常接近”所要求的值,有些浮點數“精確”,但大多數“大部分”准確。 這意味着您需要考慮測量誤差,並在計算之后牢記在心,否則可能會認為在計算值的末尾有准確的結果(不是)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM