处理0.0和-0.0,如何正确比较值

Dealing with 0.0 and -0.0, how to properly compare values

本文关键字:何正确 比较 处理      更新时间:2023-10-16

我正在研究函数来确定圆和射线是否相互相交。该功能的基础来自于本网站具体的这个页面。在第一页的注释中,作者讨论了圆和射线的相交,他列出了确定圆和射线是否相交的方法

确切的行为由正方形内的表达式决定根b * b - 4 * a * c

  • 如果该值小于0,则该行不存在

  • 如果它等于0,则该线与球体相交

  • 如果它大于0,则直线与球体相交于两点。

这就是我代码中的问题所在。这就是下面函数中double bb4ac的比较。

bool CircleRayIntersect(double ip1_x, double ip1_y, double ip2_x, double ip2_y,
                                double isc_x, double isc_y, double isc_r,
                                double &out1_x, double &out1_y,
                                double &out2_x, double &out2_y)
{
    double a,b,c; 
    double bb4ac;
    double r = isc_r;
    double dp_x;
    double dp_y;
    double mu_1;
    double mu_2;
    double p1_x = ip1_x;
    double p1_y = ip1_y;
    double p2_x = ip2_x;
    double p2_y = ip2_y;
    double sc_x = isc_x;
    double sc_y = isc_y;
    dp_x = p2_x - p1_x;
    dp_y = p2_y - p1_y;
    a = dp_x * dp_x + dp_y * dp_y;
    b = 2 * (dp_x * (p1_x - sc_x) + dp_y * (p1_y - sc_y));
    c = sc_x * sc_x + sc_y * sc_y ;
    c += p1_x * p1_x + p1_y * p1_y;
    c -= 2 * (sc_x * p1_x + sc_y * p1_y);
    c -= r * r;
    bb4ac = b * b - 4 * a * c;
    // Checks to make sure that the line actually intersects
    TRACE("  -- Checking     a: %fn", a);
    TRACE("  -- Checking bb4ac: %fn", bb4ac);
    if (abs(a) < 1E-9 || bb4ac < 0.0) 
    {
        if(bb4ac < 0.0)
        {
            TRACE("bb4ac is less than zero: %d < 0.0 = %dn", bb4ac, (bb4ac < 0.0));
            TRACE("bb4ac is less than zero: %f < 0.0 = %fn", bb4ac, (bb4ac < 0.0));
        }
        if (abs(a) < 1E-9)
            TRACE("abs(a) is less than zeron");
        mu_1 = 0;
        mu_2 = 0;
        TRACE("Ray does not intersect with circle!n");
        return FALSE;
    }
    mu_1 = (-b + sqrt(bb4ac)) / (2 * a);
    mu_2 = (-b - sqrt(bb4ac)) / (2 * a);
    out1_x = p1_x + (mu_1*(p2_x-p1_x));
    out1_y = p1_y + (mu_1*(p2_y-p1_y));
    out2_x = p1_x + (mu_2*(p2_x-p1_x));
    out2_y = p1_y + (mu_2*(p2_y-p1_y));
    return TRUE;
}

在某些情况下,当使用某些参数调用此代码时,bb4ac最终等于-0.0。当这种情况发生时,根据上面列出的规则检查bb4ac就会失效。以下是bb4ac-0.0TRACE语句的一些示例输出。

  -- Checking     a: 129.066667
  -- Checking bb4ac: -0.000000
bb4ac is less than zero: 0 < 0.0 = -1114636288
bb4ac is less than zero: -0.000000 < 0.0 = 0.000000

TRACE的输出来判断,看起来比较bb4ac0if(..)语句没有正确地解释bb4ac。看到bb4ac在用%d标志解释时被打印为-1114636288,用%f标志解释时被打印为-0.000,我不得不认为bb4acif语句中被解释为-1114636288,而不是-0.000

我怎样才能写if语句来正确地解释bb4ac作为-0.000 ?为什么它一开始会这么看呢?

假设TRACE是一个类似printf的varargs函数,使用%ddouble参数,或%fintbool参数是未定义的行为。使用正确的格式说明符或将参数强制转换为正确的类型。

您似乎很熟悉这样一个事实,即双比较永远不应该是精确的(即:你应该宽容,但你做得不太对。检查一个数字是否小于零:

if (x < -1E-9)

检查一个数字是否大于零:

if (x > 1E-9)

检查一个数字是否为零:

if (x > -1E-9 && x < 1E-9)