1

Working with floating point values it is as easy as breathing to run on approximation errors by comparing quantities which should be the same. I want to know if there is a way built in some MSDN (or even external) library for c# to ignore the problem.

An example could be: more than comparing 2 float values like this

if(myVector3.X == anotherVector3.X)

I would appreciate something like this

if(myVector3.X.isInTheNeighbourhood(anotherVector3.X))

This is not well-written, I know. That is just to simplify the explaination. What I am exactly doing is checking if a point (Vector3) lays on line segment. So basically the calculations I make are nothing more than

(x - x1)/(x2 - x1) = (y - y1)/(y2 - y1) = (z - z1)/(z2 - z1)

But these values won't be always the same, so I need to write down some code which includes a sort of tolerance a sort of Neighbourhood mathematical concept to accept values close enought to the line.

Hope that I made myself clear. Has anyone a solution for this?

Leggy7
  • 483
  • 9
  • 23
  • 2
    As an alternative to fuzzy comparisons, you might also want to consider exact predicates like [those described by J. R. Shewchuk](http://www.cs.cmu.edu/~quake/robust.html). If this is useful to you, I'll turn this comment into an answer. – MvG Jan 02 '14 at 11:19
  • I read the introduction and This can be exactly what I am searching for. I just hope that the whole article is not base on assumption too complicated for a guy with just the basics of this topic. Write your answer, sir – Leggy7 Jan 02 '14 at 11:41

3 Answers3

2

I'm proposing the use of an exact predicate. This is an alternative approach to what you actually asked, but it might be worth considering.

Suppose your three points really are at locations indicated by their respective double precision coordinates. A simple double precision computation like the one suggested in your question will likely still return a wrong result. However, there are techniques to obtain exact results to detect these cases. One technique is by turning all numbers into arbitrary precision floating point (or integer) numbers and do most of the computation using integers under the hood. Another, which should work faster on modern hardware, expresses intermediate results as sums of doubles. E.g. when computing a+b, you obtain two resulting numbers, the first is the sum as you'd usually compute it, the other is a correction term to note down the error. In many cases, the bigger terms are sufficient to make a choice, which leads to the concept of adaptive precision.

All of this, including the application to geometric predicates, has been outlined nicely by Jonathan Richard Shewchuk in his page Adaptive Precision Floating-Point Arithmetic and Fast Robust Predicates for Computational Geometry. He has a paper on it, and some C code which should be possible to adapt to C#. Or perhaps to compile in C and link to C#, thus forming a mixed language project. Note however that it makes some assumptions on how the compiler treats floating point computations. In particular, you have to be careful that certain intermediate results don't have excess precision, like the 80-bit numbers stored in a 80387-style floating point unit. I've looked into this myself recently, and the easiest solution might be asking the compiler to use SSE instructions instead of x87 ones.

In your case, you are asking whether a point p lies on the segment connecting p1 and p2. One predicate which would be very much in the spirit of that paper would be the position of a fourth point in relation to a plane spanned by three others: it is either above, below or within that plane, and the orient3d predicate can tell you which. So one possible approach would be taking four arbitrary points q1 through q4 in general position, i.e. not all in a single plane, and no three on a single line. Then you could check whether p,p1,p2,qi are coplanar (sign is zero) for all i ∈ {1,2,3,4}. If they are, then p does at least lie on the line spanned by p1 and p2. Next you could check the orientations of p,p1,qi,qj for different i,j until you find a non-zero sign, then you could see whether p,p2,qi,qj has a different sign. If it does, then p is indeed between p1 and p2, hence on the same line. If you find no i,j such that p,p1,qi,qj is non-zero, then p=p1. Likewise if you have found one non-zero sign, but the corresponding sign of p,p2,qi,qj is zero, then p=p2. It is up to you whether you want to include the endpoints of your line segment. This paragraph might not be the most elegant way to do this, but it leverages the existing orient3d implementation, so it might be easier to use than writing a new predicate from scratch.

Note that in most cases, a point will not exactly lie on a line segment, due to rounding of the point coordinates themselves. The above will only help you to reliably detect those rare cases when it does, so you can deal with them. It may also allow you to make consistent choices for other predicates as well. In the world of CGAL, this approach would be termed “exact predicates, inexact constructions”, since the predicates can be computed reliably, but the geometric objects on which they operate are still subject to approximation. If you really need points that reliably lie on the line segments, and not just close by, then an exact construction approach using some exact number type would be preferable. Or you go with the approaches the other answers suggest.

Community
  • 1
  • 1
MvG
  • 57,380
  • 22
  • 148
  • 276
  • Ok this is the most complete answer and I'm accepting it because I think that everyone struggling with this problem should have an idea of these concepts. I'm not a mathematician so I do have difficulties in understanding something, but I found it clear enought anyway – Leggy7 Jan 02 '14 at 14:08
1

You need to calculate the distance of the point to the line. That's simple mathematics. Then you decide how far of a distance "close enough" is in your case.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • 1
    Simple mathematics, but still numerically problematic in the presence of rounding errors unless you are really careful. – MvG Jan 02 '14 at 11:24
  • Distance calculation does not rely on exact numbers or equality. You have to be careful with the result and define something "close enough" because the distance will never *really* be zero, but other than that, I don't see any problems. – nvoigt Jan 02 '14 at 11:33
  • I must concede that I can't come up with a *really* problematic case just now. All cases that I can think of involve combining vectors of very different scales, and one cannot reasonably expect distances to be more precise than the largest input coordinates, so I couldn't think of any really unexpected behavior so far. – MvG Jan 02 '14 at 12:21
1

This problem is described as floating-point tolerance in this article and it points out the importance of measuring relative tolerance rather than absolute when comparing large values: http://realtimecollisiondetection.net/blog/?p=89

I've never had a situation where large floating point values are possible so I've always hard-coded a magic value into my comparisons. Eg:

if (Math.Abs(value1, value2) < 0.05f)
{
   ...
}

Which is "bad" but it works so long as value1 and value2 can't get too big.

In your case you really want to calculate the distance of the point from the line and then check that distance is small enough, rather than testing that the point is exactly on the line. I am rubbish at math myself so don't understand this but let me copy someone else's answer for calculating the shortest distance between a 3D point and a 3D line:

Vector3 u = new Vector3(x2 - x1, y2 - y1, z2 - z1);
Vector3 pq = new Vector3(x3 - x1, y3 - y1, z3 - z1);
float distance = Vector3.Cross(pq, u).Length / u.Length;

3D Perpendicular Point on Line From 3D point

Community
  • 1
  • 1
Weyland Yutani
  • 4,682
  • 1
  • 22
  • 28
  • This would surely be the easiest way of doing it. And I am sure that once I have found the right magic number it would fit all my needs. By the way I am searching for something more scalable. even if it is not ensured that I won't use this easy way because there is the chance that I won't understand answers with more complicated geometry behind :) – Leggy7 Jan 02 '14 at 11:55