我经常需要编码explicit schemes,这意味着我必须通过递增时间变量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
,除了最后一次迭代,如果我当前时间t
是这样的话那个T- dt < t < T
然后我按dt <- T-t
修改我的时间增量。
这种程序有哪些可能的缺陷或我可以改进的方法?我意识到我可能会得到一个非常小的时间增量。
是否存在可能出现的浮动问题(我应该坚持使用整数递增)?
就优化而言,我认为这种技术根本不是昂贵的,因为基本的分支预测几乎总是会跳过if
块。
我意识到我的问题并不是很好。通常dt
由CFL condition给出,即它被给出使得它与其他一些参数相比足够小。
所以从逻辑的角度来看,首先给出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
}
答案 0 :(得分:0)
首先,根本不需要补偿if (T - t < dt)
,因为它的唯一目的似乎是将最后一个值设置为t == T
,由于不等式...;t < T;...
而导致该值不会被处理for loop condition。
话虽如此,除非N
是2的幂,否则有限差分方法对浮点数的效果不佳。如果是一个人希望以0.1f的步长评估一个函数,一个人很可能会错过几个积分。
分支预测可能会跳过条件评估,但在使用流控制操作混合浮点运算时可能还会有一个惩罚/延迟。
由于累积舍入误差,优化器无法轻易确定迭代计数,不允许某些优化(循环展开甚至向量化)。
可以简单地通过线性插值来缓解不准确性:t = c * dt;
,但不完美,因为并非所有情况(dbl / N) * N == dbl
。在实践中,错误应该在 epsilon 幅度中。要获得精确的结束值,必须先计算t = (range * N) / N;
,确保range * N
不会丢弃最低有效位。
答案 1 :(得分:0)
使用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
使用执行必要的计算
每次迭代都需要t
,T
和dt
。
必须由函数更新的数据可以成为成员
类的变量或可以包含在函数参数中
列为非const引用。
顺便说一句,我最后一次调用循环外的函数而不是 为了节省分支条件的可能成本,但是因为 我发现代码组织得更好,更容易理解。
旧答案:
如你所说,最后你可以轻松获得非常小的时间增量
因为结果T/N
通常不准确
(而0.03
肯定不确切。)
我更愿意像这样开发t
和dt
:
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.
}
(请注意,如果您决定以非{0}的{0}值开始迭代,则会显示dt = (T - t)/N
。
或者,如果t
非常大,可能会稍微准确一些
(因为只要N
变得更大,t += dt
就会有效地完成dt
:
t