首页 > 解决方案 > 检查一个点是否在线段上,除了在某些情况下(舍入误差?)

问题描述

对于下面的问题,是否我是如此接近于零但将零与容差进行比较是行不通的?数字越精确,我对直线上的弧点的检查就越失败,而且越不精确,它就越有效。CAD 绘图确实有一条线段上有一个点的弧,这就是我从中获取此测试的输入坐标的地方。

class Line
{
   public Point Point1 {get;set;}
   public Point Point2 {get;set;}
   public Line(double x1, double y1, double x2, double y2)
   {
      Point1 = new Point(x1,y1); Point2 = new Point(x2,y2);
   }
}

class Point
{ 
   public double X {get;set;}
   public double Y {get;set;}
   public Point (double x, double y)
   {
       X = x; Y = y;
  }
}

//4 decimal place numbers, works
Point arcEnd = new Point(3.8421, 16.9538); // these numbers don't 
//3.84212141717697, 
//16.9538136440052
Point arcStart = new Point(4.0921, 17.2038);

//test an arc point on/off the line
Line line = new Line(3.9336, 16.9538, 3.7171, 16.9538); 
//these numbers don't 3.93362776812308, 16.9538136440053, 
//3.71712141717697, 16.9538136440054

bool on_line = Sign(line.Point1, line.Point2, arcEnd) //true

//more precise numbers, from CAD / dxf drawing for line and arc, arc end 
//point touches somewhere on the line (included in comments above, fail)
//so on_line = true for the above inputs and the Sign function gives zero, 
//but when using the commented precise numbers sign gives back 1 and the 
//value computed in sign is 3.0639866299190109E-14.




public static bool Sign(Point Point1, Point Point2, Point point)
{
    double value = (Point2.X - Point1.X) * (p.Y - Point1.Y) - (Point2.Y - Point1.Y) * (p.X - Point1.X);

    return Equals(Math.Sign(value), 0);
}

public static bool Equals(double d1, double d2, double tolerance=0.000001)
{
    double a = Math.Abs(d1 - d2);
    double b = Math.Abs(d1 * tolerance);

    if (a <= b)
    {
       return true;
    }

    return false;
}

查看公式和堆栈溢出,该算法大部分时间都有效,但发现它失败的情况,我将其追踪到包含的示例,并确定我的检查返回 Sign = 1 而不是 Sign = 0 用于上述输入和更高的精度。

标签: c#mathprecision

解决方案


你犯了两个错误。首先,您在“Return Equals(Math.Sign(value), 0);”中使用了 Sign 函数,这将为您提供任何正数的值 1 和任何负数的值 -1。这将破坏您使用宽容的尝试。其次,您尝试将差异与第一个数字“b = Math.Abs​​(d1 * tolerance)”的比率进行比较,该比率始终返回 False。我建议您将其与公差本身进行比较,就像这样。

public static bool Sign(Point Point1, Point Point2, Point point)
{
    double value = (Point2.X - Point1.X) * (point.Y - Point1.Y) - (Point2.Y - Point1.Y) * (point.X - Point1.X);
    return Equals(value, 0);
}

public static bool Equals(double d1, double d2, double tolerance = 0.000001)
{
    double a = Math.Abs(d1 - d2);
    if (a <= tolerance)
        return true;
    return false;
}

推荐阅读