我正在使用一个平均函数,它遵循以下公式
new average = old average * (n-1) / n + (new value / n)
当传递在加倍这工作伟大。我的概念证明示例代码如下所示。
double avg = 0;
uint16_t i;
for(i=1; i<10; i++) {
int32_t new_value = i;
avg = avg*(i-1);
avg /= i;
avg += new_value/i;
printf("I %d New value %d Avg %f\n",i, new_value, avg);
}
在我的程序中,我跟踪收到的消息。每次我看到一条消息,它的点击计数就增加1,这是使用timespec
的时间戳。我的目标是保持一个移动平均值(如上所述)的平均时间之间的某种类型的消息被接收。
我最初的尝试是分别对tv_nsec
和tv_sec
求平均值,如下所示
static int32_t calc_avg(const int32_t current_avg, const int32_t new_value, const uint64_t n) {
int32_t new__average = current_avg;
new__average = new__average*(n-1);
new__average /= n;
new__average += new_value/n;
return new__average;
}
void average_timespec(struct timespec* average, const struct timespec new_sample, const uint64_t n) {
if(n > 0) {
average->tv_nsec = calc_avg(average->tv_nsec, new_sample.tv_nsec, n);
average->tv_sec = calc_avg(average->tv_sec, new_sample.tv_sec, n);
}
}
我的问题是我使用的是整数,值总是向下舍入,我的平均值是远远偏离的。是否有更智能/更简单的方法来平均timespec
读数之间的时间?
3条答案
按热度按时间pes8fvy91#
下面是我多年来一直在生产S/W中使用的代码。
主要的想法是,仅仅因为
clock_gettime
使用struct timespec
并不意味着它必须到处“携带”:1.更容易转换为
long long
或double
,并在从clock_gettime
获得 * 这些 * 值后立即传播它们。1.所有 * 进一步 * 数学是简单的加/减,等等。
clock_gettime
调用的开销使转换中的乘/除时间相形见绌。使用固定纳秒值还是小数秒值取决于具体的应用程序。
在您的情况下,我可能会使用
double
,因为您已经有了适用于此的计算。不管怎样,这是我使用的:
tkqqtvp12#
1.使用更好的整数数学。
new_value < 0
可能,则使用有符号数学,否则下面不需要int64_t
转换。示例代码:
1.回顾一下,在两个部分中做平均的整个想法是有缺陷的。而是使用64位纳秒计数。直到2263年。
建议代码:
如果一定要形成一个
struct timespec
的平均值,很容易做到当average >= 0
。kkbh8khc3#
如果你想保留整数,你需要用定点数字做数学运算。这有点棘手
在大多数情况下,这是通过在小数点后保留N位来完成的。不幸的是,对于
timespec
结构,小数点将是从0到999,999,999的数字,这不是2的幂。我仍然认为你能做到。使用支持
__int128
类型的编译器,可以先将timespec
转换为__int128
,然后根据该数字进行数学运算。现在,您可以使用
t
计算平均值,其精度为小数点后9位。如果你想要更精确一点,比如说另一个2位数,你可以用途:即将左和右再乘以100。
_注意:非常大的数字可能需要转换;因此,上面的代码可能需要编写为确保数学按预期工作:
没有办法写一个完整的
__int128
文字数(我知道),你可能需要使用一些数学来使用这样的。现在
t
作为11位数的精度。用这些固定点值做数学运算--即加法和减法都很好,乘法和除法需要额外的工作...您仍然会有类似的精度错误,只是比使用普通整数少得多。只是普通整数和定点数之间的数学比较难理解。如果使用double就足够了,那么克雷格的解决方案是最简单的。