1

前段时间我写了一个算法来找到两条线段之间的交点。不过,回过头来看它,我注意到在两个不同的地方除以零是可能的。从逻辑的角度来看,我认为这样做是可以的,但是如果这样做,程序当然会崩溃。这是算法:

class Segment{
    public:

    Vector first, second;


    bool getIntersection(const Segment arg, Vector * intersect = NULL)const{
        Segment segment(b - a, arg.b - arg.a);
        double s, t;

        // These two equations run the risk of division by zero
        s = (-segment.a.y * (a.x - arg.a.x) + segment.a.x * (a.y - arg.a.y)) 
             / (-segment.b.x * segment.a.y + segment.a.x * segment.b.y);
        t = ( segment.b.x * (a.y - arg.a.y) - segment.b.y * (a.x - arg.a.x)) 
             / (-segment.b.x * segment.a.y + segment.a.x * segment.b.y);

        if (s > 0.0 && s < 1.0 && t > 0.0 && t < 1.0)
        {
            // Collision detected
            if (intersect != NULL)
            {
                intersect->x = a.x + (t * segment.a.x);
                intersect->y = a.y + (t * segment.a.y);
            }
            return true;
        }
        return false; // No collision
    }

};

如果除数后面的值(并且可能结果是)全为零,则在计算s和的地方除以零是可能的。t从几何学的角度来看,这意味着两条线相互平行;在这种情况下,不应将平行线视为相交,即使它们完全重叠。

有没有办法让这个函数除以零?当然,假设它不会影响算法背后的逻辑(我认为任何带有 NAN 或 INF 的东西都不会导致冲突)?或者我应该使用更好的线段相交算法吗?

4

2 回答 2

2

我建议分别计算两个分子和分母:

double num1 = (-segment.a.y * (a.x - arg.a.x) + segment.a.x * (a.y - arg.a.y));
double num2 = ( segment.b.x * (a.y - arg.a.y) - segment.b.y * (a.x - arg.a.x));
double denom = (-segment.b.x * segment.a.y + segment.a.x * segment.b.y);

然后将分母归一化为非负:

if (denom < 0) { denom = -denom; num1 = -num1; num2 = -num2; }

最后检查

if (0.0 < num1 && num1 < denom && 0.0 < num2 && num2 < denom) {
    double t = num2 / denom;
    ...
}

这不仅解决了“分母为零”的问题,而且还避免了分母非常小时可能出现的溢出问题。

于 2013-04-08T08:04:18.580 回答
1

如果您想以编程方式执行它会有点困难,因为C++不支持divide_by_zero-exception 或类似的东西。

根据这里的这个线程,这是一个硬件错误,被操作系统检测到并返回给进程。

引用:

这不是个例。这是一个错误,在硬件级别确定并返回给操作系统,然后操作系统以某种特定于操作系统的方式通知您的程序(例如,通过终止进程)。

你可以catch通过信号处理程序来实现。除以零将创建一个SIGFPE. 但这可能不是很有用或可靠。

于 2013-04-07T18:54:39.053 回答