views:

798

answers:

11

hi,

I am writing a code in C++ and want to compute distance between two points. Question 1:

I have two points P(x1, y1, z1) and Q(x2, y2, z2) , where x, y and z are floats/doubles.

I want to find the distance between these two points. One way to do it is :

square_root(x_diff*x_diff + y_diff*y_diff + z_diff*z_diff)

But this is probably not the most efficient way . (e.g. a better formula or a ready made utility in math.h etc )

Question 2:

Is there a better way if I just want to determine if P and Q are in fact the same points?

My inputs are x, y and z coordinates of both the points.

Thank you

+26  A: 

Do you need the actual distance? You could use the distance squared to determine if they are the same, and for many other purposes. (saves on the sqrt operation)

Autopulated
This is the correct answer for comparisons--and many other vector operations where you think you need distance. Use distance squared! For example, instead of writing `sqrt(dx*dx + dy*dy + dz*dz) < epsilon` you write `dx*dx + dy*dy + dz*dz < epsilonsquared`. Mathematically equivalent, and much faster.
Rex Kerr
+4  A: 

No there is no better way.

The implementation of square_root might be optimised.

If you are comparing two distances and want to know the longer, but do not care about what the actual distance is, then you can simply ingore the square-rooting step completely and manipulate your distances still squared. This would be applicable to comparing two pairs of points to determine if they are the same distance apart, for example.

Will
thank you Will.
memC
I was going to say it's highly unlikely that someone could come up with a better square root function than C++'s built in one. But see pheelicks's answer. Maybe you can!
MatrixFrog
+2  A: 

Q2 answer: x1 = x2 and y1 = y2 and z1 = z2 if the points are the same.

Taking in consideration that you store points as float/double you might need to do the comparison with some epsilon.

Victor Hurdugaci
+6  A: 

No, there is no more efficient way to calc the dist. Any treatment with special cases p.x==q.x etc. will be slower on average.

Yes, the fastest way to see if p and q are the same points is just comparing x, y, z. Since they are float, you should not check == but allow for some finite, small difference which you define.

+7  A: 

Is there a better way if I just want to determine if P and Q are in fact the same points?

Then just compare the coordinates directly!

bool areEqual(const Point& p1, const Point& p2) {
     return fabs(p1.x - p2.x) < EPSILON &&
            fabs(p1.y - p2.y) < EPSILON &&
            fabs(p1.z - p2.z) < EPSILON;
}
Mehrdad Afshari
+1, saved my typing and testing (even before the IDE spawned, hehe)
mlvljr
thanks Mehrdad.. that answers my second question.
memC
You're using an `EPSILON`-sized cube to determine proximity. Reasonable, but it's quicker to check `(fabs(p1.x - p2.x) + fabs(p1.y - p2.y) + fabs(p1.z - p2.xz)) < EPSILON_3D` - single comparison. (with `EPSILON <= EPSILON_3D <= 3*EPSILON`)
MSalters
@MSalters: That's not the same thing though. My way of doing it is more precise (max diff in each dimension is `EPSILON` whereas in your solution, `p1.x-p2.x` can be `3*EPSILON` and two other dimensions stay equal). I wouldn't bet on that being faster (it has 3 additions instead of 3 comparisons) unless I benchmark on a specific machine/compiler/platform. Either case, that's a micro-optimization and bears all of the warnings associated with it.
Mehrdad Afshari
No, the "precision" is determined exclusively by the size of the volume around `p1` considered equal to `p1`. Both our tests use a cube, mine is just rotated. You can get exactly the same box size by an appropriate choice of EPSILON_3D (`sqrt(3) * EPSILON` IIRC), and thus exactly the same precision. As to speed; test it if you don't believe it.
MSalters
MSalters: Yes, you're right about `sqrt(3) * EPSILON`. I was thinking about `3 * EPSILON` which is less precise.
Mehrdad Afshari
+1  A: 

There are faster ways to get an approximate distance but nothing built into the standard libraries. Take a look at this article on FlipCode that covers the method for fast 2D distances. It essentially collapsed the sqrt function into a compound linear function that can be quickly calculated but isn't 100% accurate. However, on modern machines these days fpmath is fairly fast so don't optimize too early, you might find that you're fine taking your simple approach.

Trevor Tippins
+4  A: 

You might find this article interesting:

http://www.codemaestro.com/reviews/9

It describes how the square root was calculated in the Quake 3 engine, claiming that on some CPU's it ran 4 times as fast as the sqrt() function. Not sure whether it'll give you a performance boost in C++ nowadays - but still an interesting read

pheelicks
+6  A: 

You may try to use SSE extensions. For example, you can init two vectors A(x1,y1,z1) and B(x2,y2,z2):

_m128 A = _mm_set_ps(x1, y1, z1, 0.0f)
_m128 B = _mm_set_ps(x2, y2, z2, 0.0f)

Then compute diff using _mm_sub_ps:

__m128 Diff = _mm_sub_ps(A, B)

Next compute sqr of diff:

__m128 Sqr = __mm_mul_ps(Diff, Diff)

And finally:

__m128 Sum = add_horizontal(Sqr)
__m128 Res = _mm_sqrt_ss(Sum)

Res[0] will be filled with your answer.

P.S. add_horizontal is a place for optimization

shuvalov
Many modern compilers use SSE and SSE2 by themselves.
AndreyT
IIRC SSE3 includes some kind of add_horizontal operation. It might only have a double-precision version though.Also, you used _mm_set_ps instead of _mm_sub_ps when you calculate Diff. I don't have enough ninja power to correct it myself :)
Peter
There's a horizontal add for single-precision as well (`haddps`, though it doesn't sum the entire vector in one operation; it needs to be invoked twice to sum all four elements. On some architectures, this can be slower than an equivalent sequence of shuffles and adds, depending on what other instructions are in flight).
Stephen Canon
+1  A: 

Note that when using sqrt(dx*dx+dy*dy+dz*dz) the sum of squares might overflow. hypot(dx, dy) computes a distance directly without any chance of overflow. I'm not sure of the speediest 3d equivalent, but hypot(dx, hypot(dy, dz)) does the job and won't overflow either.

Darius Bacon
I always assume hypot was literally just doing sqrt(x*x + y*y). What does it do that's different and prevents overflow?
MatrixFrog
@MatrixFrog: a typical simple implementation of hypot checks the scaling of `x` and `y`; if they are well scaled, it just does `sqrt(x*x + y*y)`, but if they are poorly scaled (such that that computation would result in undue overflow or underflow), it rescales them by some known value, then does the computation, then scales the final result. More sophisticated implementations are possible, especially when sub-ulp accuracy or correct rounding is the goal. I would suggest looking at some of the open source implementations if you're curious.
Stephen Canon
A: 

As far as Question 1 goes, the performance penalty is the calculation of the square root itself. The formula for calculating the distance using the square root of paired coordinate differences is what it is.

I would highly recommend to read this A-M-A-Z-I-N-G square root implementation by John Carmack of ID software he used in his engine in Quake III. It is simply MAGICAL.

Theo Zographos
It's a clever implementation, but not really "amazing" to someone in the field. It's also slower than using the reciprocal square root instructions on all modern hardware.
Stephen Canon
John Carmack didn't write that algorithm, actually. I forget who, but it wasn't him.
GMan
+1  A: 

The GNU Scientific Library defines gsl_hypot3 that computes exactly the distance you want in the first part of your question. Kind of overkill compiling the whole thing just for that, given Darius' suggestion, but maybe there's other stuff there you want.

Charles Stewart