双变量作为循环计数器

Double variable as loop counter

本文关键字:循环 计数器 变量      更新时间:2023-10-16

我经常需要编写显式方案,这意味着我必须通过增加时间变量t <- t+ dt来观察函数的演变。因此,我的循环在dt:上增加是很自然的

int N = 7;
double t=0., T = 1., dt=T/N; // or I could have dt=0.03 for example
for(; t<T; t+= dt){
   if(T - t < dt){
      dt = T-t;
   }
   //some functions that use dt, t, T etc
} 

这背后的原理是,我在每一步都将t增加一个常数dt,但在最后一次迭代中除外,其中如果我的当前时间tT- dt < t < T,那么我将时间增量修改为dt <- T-t

这种程序可能存在哪些陷阱,或者我可以改进它的方法是什么?我确实意识到我可能会得到一个很小的时间增量。

是否存在可能出现的浮动问题(我应该坚持整数递增吗(?

在优化方面,我认为这种技术根本不昂贵,因为基本分支预测几乎总是跳过if块。

编辑

我意识到我的问题不是很好。通常,dt是由CFL条件给出的,即,与一些其他参数相比,它足够小。

因此,从逻辑的角度来看,首先给出了dt,然后我们可以定义一个整数N=floor(T/dt),用直到N的整数循环,然后处理剩余的时间间隔N*dt --- T

代码是:

double dt = //given by some function;
double t=0., T = 1.;
for(; t<T; t+= dt){
   if(T - t < dt){
      dt = T-t;
   }
   //some functions that use dt, t, T etc
} 

首先,不需要补偿if (T - t < dt),因为它的唯一目的似乎是将最后一个值设置为t == T,由于for循环条件中的不等式...;t < T;...,因此不会处理该值。

也就是说,除非N是2的幂,否则有限差分法在浮点运算中效果不佳。例如,如果希望以0.1f的步长评估函数,则很可能会错过几个积分点。

分支预测可以跳过条件评估,但是在将浮点操作与流控制操作混合时也可能存在惩罚/延迟。

由于累积的舍入误差,优化器可能无法轻松确定迭代次数,从而不允许进行某些优化(循环展开甚至矢量化(。

通过线性插值t = c * dt;可以简单地减轻误差,但并不完全,因为并非所有情况都是(dbl / N) * N == dbl。在实践中,误差应在ε幅度内。为了获得确切的结束值,必须计算t = (range * N) / N;,以确保range * N不会丢失最低有效位。

有了新信息,dt必须设置为固定的预定值(至少对于除最后一步外的所有步骤(,以下是我的建议:

double T0 = 0.0;
double T  = 1.0;
int    N  = floor((T - T0)/dt);
double t  = T0;
for (int step_number = 0; step_number < N; ++step_number, t += dt)
{
  t = T0 + step_number * dt;
  do_one_step(t, T, dt);
}
if (t < T)
{
  do_one_step(t, T, T - t);
}

函数do_one_step使用tTdt。函数必须更新的数据可以成为成员类的变量或可以包含在函数参数中列为非常数引用。

顺便说一句,我有最后一次调用循环外的函数,而不是为了节省分支条件的可能成本,但因为我发现这样代码组织得更好,更容易理解。


老答案:

你可以很容易地在最后得到一个非常小的时间增量,正如你所说,因为结果CCD_ 27通常不精确(0.03当然不准确(。

我更喜欢这样开发tdt

int    N  = 7;
double t  = 0.0;
double T  = 1.0;
double dt = (T - t)/N;
for (int step_number = 0; step_number < N; ++step_number, t += dt)
{
  // ... calculations with t, T, dt, etc.
}

(请注意,上面写着dt = (T - t)/N,以防您决定以非零值t开始迭代。(

或者,如果N非常大,可能会稍微准确一点(因为一旦t变得更大,t += dt就必须有效地将dt取整(:

int    N  = 7;
double T  = 1.0;
double dt = T/N;
for (int step_number = 0; step_number < N; ++step_number)
{
  double t = T0 + step_number * dt;
  // ... calculations with t, T, dt, etc.
}