AbG*_*tor 2 java algorithm math floating-point nan
用浮点数做数学时,我得到了一个令人困惑的结果.我的代码永远不会产生负数,产生负数,当我尝试取平方根时会导致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));
}
Run Code Online (Sandbox Code Playgroud)
对于某些背景:我正在尝试优化计算信号数据的滚动均方根(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;
}
Run Code Online (Sandbox Code Playgroud)
这段代码很慢,因为它在每一步都从数组中读取整个窗口,而不是利用窗口中的重叠.预期的优化是使用该重叠,通过删除最旧的值并添加最新值.
我仔细检查了新版本中的数组索引.它似乎按预期工作,但我在那个领域肯定是错的!
更新:
使用我们的数据,足以将类型更改sum
为double.不知道为什么我没有发生这种情况.但我留下了负面检查.而且,我还能够实现一个sol'n,其中每400个样本重新计算总和,提供了极大的运行时间和足够的准确性.谢谢.
这是一个浮点错误问题吗?
是的.由于四舍五入,在减去先前的加数后,您可能会得到负值.
例如:
float sum = 0f;
sum += 1e10;
sum += 1e-10;
sum -= 1e10;
sum -= 1e-10;
System.out.println(sum);
Run Code Online (Sandbox Code Playgroud)
在我的机器上,这打印
-1.0E-10
Run Code Online (Sandbox Code Playgroud)
即使在数学上,结果也是零.
这是浮点的本质:1e10f + 1e-10f
给出与之完全相同的值1e10f
.
就缓解策略而言:
double
而不是float
增强精度.