【发布时间】:2018-04-12 18:22:00
【问题描述】:
我正在应用 Exponential Moving Average 作为我的音频应用程序中平滑参数的过滤器:
a0 = 0.01
z += a0 * (input - z);
Here's代码和前50步:
#include <iostream>
int main ()
{
double a0 = 0.1;
double input = 0.8;
double z = 0.0;
std::cout << "init z: " << z << std::endl << std::endl;
for(int i=0; i < 50; i++) {
z += a0 * (input - z);
std::cout << z << std::endl;
}
std::cout << std::endl << "final z: " << z << std::endl;
}
我需要检查前一个平滑值是否与当前值相同,这意味着过滤器已经“完成”了它的平滑过程,并且该值将始终相同。
但是z 总是不同于epsilon 和input,所以我不能检查input == z 它总是错误的。 Here's 一个例子,无限循环。
z 和 input 之间的 epsilon 是多少?所以如果它在那个范围内,我可以检查并避免进一步的操作。
【问题讨论】:
-
z和input之间的乘法 epsilon 可能更容易表征,并且计算更稳定。 -
从哲学上讲:),打破循环的决定有两个代价:结果与“真实”值的距离和迭代次数;直到您在脑海中弄清楚总成本是如何形成的(因此当达到最大成本并且循环中断时),问题是并且总是会是不正确的。
-
拒绝将新的
z值与之前的z值进行比较是错误的。通过使用浮点运算的属性,可以编写代码来精确测试添加a0 * (input - z)是否会改变z的值,但是该代码比简单地复制z和比较新旧值。在没有直接比较或精确测试的情况下,替代方法是简单地测试与z相比增量是否“小”。那么“小”是一个偏好问题或者是特定于应用程序的问题,并没有明确的答案。 -
@markzzz:我可以但不会。这是一个相当大的麻烦。您可以使用 frexp 和 ldexp 计算
z的 ½ ULP,然后将a0 * (input - z)与它进行比较。然后,如果它恰好是 ½ ULP,则必须确定z的有效数字的低位奇偶校验。而且您必须处理各种特殊情况,例如次正规。这将是过多的代码。与z之前的值相比,这是一个更好的解决方案。 -
@markzzz:首先你说你不想避免引入一个新变量,现在你说你想节省 CPU。执行建议给您的任何测试所需的操作都很快,肯定比通过检查所涉及的浮点特性而不是直接执行操作来计算在添加增量时
z是否会改变所需的操作要快。知道自己在说什么的人给了你很好的解决方案。使用它们。
标签: c++ floating-point precision moving-average