在C ++中缩放double时的精度差异

时间:2015-07-30 12:35:37

标签: c++ double precision

是否有必要缩放双值,还是在使用真实世界值时更准确?哪个是用double进行计算以获得最佳精度的最佳范围?

即。我定义了一个三次贝塞尔曲线。我应该使用真实世界位置值作为三次曲线,还是应该在计算时使用值的标准化大小,然后在我想要读取真实世界值时将它们放大?

我举一个例子,看看这段代码:

void CCubic::getTAtDistance(const CCubicPoint& pFrom, const double& distance, double& t)
{

    CPoint pEnd = getP(t);

    double cLength = (pEnd - pFrom).getLength();
    int compare = GLOBAL::DoubleCompare(cLength, distance);
    if(compare > 0)
    {
        t-=(t - pFrom.t)*0.5;
        getTAtDistance(pFrom, distance, t);
    }
    else if(compare < 0)
    {
        t+=(t - pFrom.t)*0.5;
        getTAtDistance(pFrom, distance, t);
    }//else if
}

此方法计算三次曲线上距离三次曲线上另一个点的距离的点。

  • pFrom是计算距离的点。
  • t将逐步计算并定义新的 在增量完成时,指定距离处的曲线上的点。
  • 方法getP计算并返回指定t处三次曲线上的一个点。

最初在调用方法时,t设置为1.0(曲线结束)。 由于三次贝塞尔曲线不是线性的,我需要逐步计算距离点pFrom指定距离的最近点。

在曲线上创建所有点的代码如下所示:

void CCubic::initEvenPointList(double distance, double offset)
{
    //TODO: Check if r can be 0 in the 1/r code, and how to handle/show it.
    lPoints.clear();
    minRadius = DBL_MAX;

    double t = 0;
    CCubicPoint ccP = getCubicPoint(t);
    lPoints.push_back(ccP);
    if(ccP.radius<minRadius) minRadius = ccP.radius;

    if(offset>0)
    {
        t = 1.0;
        getTAtDistance(getCubicPoint(0), offset, t);
        ccP = getCubicPoint(t);
        lPoints.push_back(ccP);
        if(ccP.radius<minRadius) minRadius = ccP.radius;
    }//if


    std::cout << "CCubic::initEventPointList -- Starting loop\n";
    while(t<1.0)
    {
        double newT = 1.0;
        getTAtDistance(ccP, distance, newT);
        if(newT>1) break;
        t = newT;
        ccP = getCubicPoint(t);
        lPoints.push_back(ccP);
        if(ccP.radius<minRadius) minRadius = ccP.radius;
    }

    ccP = getCubicPoint(1.0);
    lPoints.push_back(ccP);
    if(ccP.radius<minRadius) minRadius = ccP.radius;


    std::cout << "P(" << 0 << "): t = " << lPoints[0].t << "\n";
    double d = 0;
    for(int i=1; i<lPoints.size(); i++)
    {
        d+= (lPoints[i] - lPoints[i-1]).getLength();
        std::cout << "P(" << i - 1<< "): t = " << lPoints[i].t << ", d = " << d*400 << "\n";
    }//for
}

如果我在realworld值中定义一个三次贝塞尔曲线:

  • A(-400,0,0)(起点)
  • B(0,-200,0)(A的控制点)
  • C(0,-200,0)(D的控制点)
  • D(400,0,0)(终点)

并将距离设置为25.

我得到大约34分,两者之间的距离正确。一切都还可以。

然后我注意到如果我将三次贝塞尔曲线定义为标准化并将其缩放(最大值为1.0),即:

  • A(-1,0,0)(起点)
  • B(0,-0.5,0)(A的控制点)
  • C(0,-0.5,0)(D的控制点)
  • D(1,0,0)(终点)

然后我将距离设置为25/400(比例为400)。 如果计算整个曲线,我在缩放后只得到大约4个点。这不应该发生在数学中。所以应该有一个舍入错误,或者我的错误代码。

我给你getCubicPoint和getP的代码,以及DoubleCompare:

CPoint CCubic::getP(double f) const
{
    CPoint rP = pA*pow(1-f, 3) + pB*3*f*pow(1-f, 2) + pC*3*(1-f)*pow(f, 2) + pD*pow(f,3);

    return rP;
}

CCubicPoint CCubic::getCubicPoint(double f) const
{
    CPoint cP = pA*pow(1-f, 3) + pB*3*f*pow(1-f, 2) + pC*3*(1-f)*pow(f, 2) + pD*pow(f,3);
    CPoint pI = (pB - pA)*3 + (pA + pC - pB*2)*6*f + (pD + pB*3 - pC*3 - pA)*3*pow(f,2);
    CPoint pII = (pA + pC - pB*2)*6 + (pD + pB*3 - pC*3 - pA)*6*f;

    double r = (pI.x*pII.y - pII.x*pI.y) / pow((pow(pI.x,2) + pow(pI.y, 2)), 3.0/2.0);
    r = 1/r;
    if(r<0) r = -r;
    pII = pI.getNormal(true);       //Right normal
    pII = pII.getNormalized();

    return CCubicPoint(cP, pII, r, f);
}


int GLOBAL::DoubleCompare(double A, double B)
{
    if(abs(A - B) < std::numeric_limits<double>::epsilon()) return 0;
    if(A < B) return -1;
    return 1;
}

1 个答案:

答案 0 :(得分:1)

double有11位指数和53位精度。这意味着任何有限双精度具有相同的精度,无论其大小是4,400或4e300。相对于“实际”范围与任何其他幅度的归一化并不重要。

唯一需要注意的是,与你合作的数字是不同的数字。例如,在浮点路径1e300 + 1 == 1e300中,因为没有足够的精度来表示1

我认为这种差异导致了你的问题。在DoubleCompare内:

if(abs(A - B) < std::numeric_limits<double>::epsilon()) return 0;

epsilon被定义为1.0 处的最小可表示浮点差。我知道您的意图是允许浮点错误,但不同的幅度将需要不同的相对误差。布鲁斯道森的"Comparing Floating Point Numbers"有更多细节和其他技术的评论。

(在这里使用abs也让我有点紧张,因为C中的abs只取整数。因此,你是否得到一个浮点绝对值取决于什么标题您已经包含了以及之前是否在代码中执行了using namespace std;using std::abs;。也许我只是偏执狂,但我更喜欢C fabs或明确的{{ 1}}。)

您的代码不够完整,无法编译,所以我不确定,但我认为改进与std::abs的比较会为您提供一致的结果。