Over the years I've came across a few algorithms to calculate the angle between two 3D vectors. As the image shows I've discarded acos and asin ones for their corner case issues.
I'm now down to two of them.
I've read about them in different places but in this question, this answer states that atan method is better on smaller angles. I tried on my end but did not notice any issue on smaller angles. And I would like more insight about why one could be better than another. Or maybe insight about how ought I test them or which reference should I compare their output to.
I actually have a bias towards atan2 solution, Version B, because:
That said if, indeed Version A is indeed more accurate I'll consider it for the operations I'm doing, instead of B, but I would like to understand better the reasons for it, maybe visualize it on my end too, and this way be more confident about adopting Version A, instead of Version B.
Here are live code versions of all: repl.it
Thanks
If you want to compare methods empirically, you need a ground truth to compare against that is the actual accurate value. You need to compute this with arbitrary precision math, although you can use one of the existing algorithms to do it. Here's an example:
import mpmath
mpmath.mp.dps = 50
def angle_atan_true(x, y):
xnorm = mpmath.sqrt(mpmath.fdot(x, x))
ynorm = mpmath.sqrt(mpmath.fdot(y, y))
xprod = [mpmath.fmul(n, ynorm) for n in x]
yprod = [mpmath.fmul(n, xnorm) for n in y]
plus = [mpmath.fadd(*n) for n in zip(xprod, yprod)]
minus = [mpmath.fsub(*n) for n in zip(xprod, yprod)]
norm1 = mpmath.sqrt(mpmath.fdot(plus, plus))
norm2 = mpmath.sqrt(mpmath.fdot(minus, minus))
return mpmath.fmul(2, mpmath.atan2(norm2, norm1))
a = [1, 0, 0]
b = [1, 0, 1e-8]
print("a", a, ", b", b)
print("atan_true:", angle_atan_true(a, b))
a [1, 0, 0] , b [1, 0, 1e-08]
atan_true: 0.0000000099999999999999998758922749679513924973721774310726
(That won't be the exact value either, but it should be accurate to at least 40 places, more than sufficient to validate the machine-precision versions.)
You will also want to look at a variety of inputs, not just a handful. Looping through lots and plotting the error can be helpful.