在C++中将一个区间分成n个相等的部分

Dividing an interval into n equal parts in C++

本文关键字:区间 一个 C++      更新时间:2023-10-16

我在c++中遇到了一个从未遇到过的浮点运算问题(使用doubles),所以我想知道人们通常是如何处理这类问题的。

我试图将极坐标中的曲线表示为一系列点对象(点只是一个在三维中保存点坐标的类)。表示曲线的点集合存储在(点*的)向量中。我所代表的曲线是一个函数r(θ),我可以计算它。该函数定义在[0,PI]中包含的θ范围内。我将PI表示为4.0*atan(1.0),并将其存储为双精度。

为了表示曲面,我指定了所需数量的点(n+1),我目前使用的是n=80,然后我确定了将[0,PI]划分为80个相等间隔(由n+1=81个点表示)所需的θ间隔。所以dTheta=PI/n。dTheta是一个二重。接下来,我将坐标分配给我的点。(请参阅下面的示例代码。)

double theta0 = 0.0; // Beginning of inteval in theta
double thetaF = PI; // End of interval in theta
double dTheta = (thetaF - theta0)/double(nSegments); // segment width
double theta = theta0; // Initialize theta to start at beginning of inteval
vector<Point*> pts; // Declare a variable to hold the Points.
while (theta <= thetaF)
{
// Store Point corresponding to current theta and r(theta) in the vector.
pts.push_back(new Point(theta, rOfTheta(theta), 0.0));
theta += dTheta; // Increment theta
}

rofTheta(θ)是计算r(θ)的函数。现在的问题是,最后一点不知何故不满足最后一次进入循环的(theta<=thetaF)要求。实际上,在最后一次通过循环之后,θ比PI稍微大一点(就像PI+1e-15)。我该如何处理?没有为theta>PI定义函数。一个想法是只测试(θ>PI)和(θ<(PI+delta)),其中delta非常小。如果这是真的,我可以设置θ=PI,获取并设置相应点的坐标,然后退出循环。这似乎是一个合理的问题,但有趣的是,我以前从未遇到过这样的问题。我一直在使用gcc 4.4.2,现在我使用的是gcc 4.8.2。这可能是问题所在吗?处理这种问题的正常方法是什么?谢谢

如果您可以通过计算下一个值,则永远不要通过添加增量来迭代具有浮点值(theta)的范围

theta = theta0 + idx*dTheta.

使用整数步数来控制迭代,并按指示计算浮点值。

如果dTheta与整个间隔相比很小,则会累积错误。

您不能插入范围[theta0,thetaF]的最后一个计算值。这个值实际上是θ=θ0+n*(dTheta+误差)。跳过上次计算的值,改为使用taF。

我可以尝试的:

while (theta <= thetaF)
{
// Store Point corresponding to current theta and r(theta) in the vector.
pts.push_back(new Point(theta, rOfTheta(theta), 0.0));
theta += dTheta; // Increment theta
}
if (theta >= thetaF) {
pts.push_back(new Point(thetaF, rOfTheta(thetaF), 0.0));
}

您可能想用pts.length() == nSegments来cehck if语句,只需进行实验,看看哪一个能产生更好的结果。

如果您知道theta将有81个值,那么为什么不运行for循环81次呢?

int i;
theta = theta0;
for(i = 0; i < nSegments; i++) {
pts.push_back(new Point(theta, rOfTheta(theta), 0.0));
theta += dTheta;
}

首先:去掉裸指针:-)

你知道你有多少段,所以不要在while块中使用θ的值:

for (auto idx = 0; idx != nSegments - 1; ++idx) {
// Store Point corresponding to current theta and r(theta) in the vector.
pts.emplace_back(theta, rOfTheta(theta), 0.0);
theta += dTheta; // Increment theta
}
pts.emplace_back(thetaF, /* rOfTheta(PI) calculated exactly */, 0.0);
for (int i = 0; i < nSegments; ++i)
{
theta = (double) i / nSegments * PI;
…
}

此:

  • 产生正确的迭代次数(因为循环计数器保持为整数)
  • 不累积任何误差(因为theta每次都是新计算的),并且
  • 在最终迭代中产生精确的期望值(好吧,PI,而不是π)(因为(double) i / nSegments将恰好是一)

不幸的是,它包含一个除法,这通常是一个耗时的指令。

(循环计数器也可以是double,这将避免循环内从转换为double。只要使用整数值,它的算术运算就会精确,直到迭代次数超过2次。)