There is a standard formula for calculating the angle between vectors:

The formula for calculating the angle between vectors

And there are two vectors, the angle between which can not be calculated, since the right side of the equation is less than -1. These vectors are:

var x1 = -0.045797169475341334, y1 = -0.9989507591808752; var x2 = 0.04579716947534099, y2 = 0.9989507591808753; 

In summary, the expression:

 (x1 * x2 + y1 * y2) / Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2)) 

gives the result: -1.0000000000000002

And if we take the arc cosine of this number, it will be NaN , which is understandable, since it is defined on the interval from -1 до 1 .

How can I adjust the formula so that this error does not exist?

  • You have in the initial formula in the numerator module (absolute value). The code should have a call to the abs function for x1 * x2 + y1 * y2 . - Mark Shevchenko
  • Well, abs certainly won't help here. You can simply insert a type check if (r>1 || r< -1) r = +-1 (such pseudo-code). Otherwise rounding errors can not be corrected. The easiest way to override the acos function is to write your own with checking for an out of range [-1, 1] . - andy.37
  • 2
    Or use s-shny atan2 . He does not give such errors. - andy.37
  • In general, when the angle between the vectors is close to Pi, significant errors are possible associated with rounding. - andy.37
  • @ andy.37 How to do with atan2 ? - Khusamov Sukhrob

2 answers 2

There are at least 2 solutions to the problem. Both of them require that both vectors have a nonzero length, but for a vector with zero length, the question itself about any angle is not completely correct, and you should know better how you handle this situation.

1) calculate the intermediate value:

 r = (x1 * x2 + y1 * y2) / Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2)) 

Check for hitting the range [-1, 1]:

 if (r < -1) r = -1; if (r > 1) r = 1; 

We calculate arccosine. This eliminates rounding errors that "knock out" r from the range.

2) Using the atan2 function

 phi = Math.atan2(y2, x2) - Math.atan2(y1, x1); 

If necessary, we bring the received corner to the necessary range (0-180grad or -90 - +90)


I would write like this:

 if (phi < 0) phi += Math.PI * 2; if (phi > Math.Pi) phi -= Math.PI; 

Both algorithms will give an error when x1 = y1 = 0 or x2 = y2 = 0, as described above. The variant with atan2 , IMHO is preferable, because, for example, when x1 = y1 = 1e + 10, x2 = y2 = 1e-10, the accuracy of calculations in the first variant will be near-zero (e10 is taken just for example, you might need significantly more ).


Mathematical functions are specified for Java, for .NET names contain an uppercase letter: Math.Sqrt , Math.Atan2 , etc.

  • The .NET function is called Math.Sqrt (with a capital letter). And instead of Pow it's better to multiply by yourself manually. Although, can the author mean Java? - VladD
  • @VladD, I copied the code of the vehicle, due to complete ignorance of .NET and C #. Just the problem here is not dependent on the language. If you can correct the answer in terms of C # syntax, please correct. - andy.37
  • Done, edited. - VladD
  • Thanks for the answer! I liked atan2 more. Probably I will use it. In my version with atan2 you need to calculate the module. Negative angle is not needed. - Khusamov Sukhrob
  • @KhusamovSukhrob a good question is a normal answer. Only not a module. atan2 returns a value in the range [-pi, pi]. As a result, you will have the range [-2 * pi, 2 * pi]. It must be normalized either to the range [0, 2pi], or to [-pi, pi], or to [0, pi] (if the order of the vectors is unimportant). It is not difficult. If necessary, I will add, but it's better to think of yourself? - andy.37

If there is a desire to use the formula for the difference of angles (which is understandable from the question), then you can use half argument tangents with a period of 360: phi = 2(Math.atan2(y2, x2+r2) - Math.atan2(y1, x1+r1));

Let's check this formula on the data x1 = sqrt (3), y1 = 1, x2 = - sqrt (3), y2 = 1 r1 = r2 = 2:
The correct answer is: cos (fi) = -1/2, fi = 120 .
According to the formula for the difference:
fi1 = 2arctg (1 / (2 + sqrt (3))) = 30, fi2 = 2arctg (1 / (2-sqrt (3)) = 150, fi = fi2-fi1 = 120 .
It remains only to convert the interval to choose:
either mod (fi, 360) or mod (fi + 180,360) -180

In the same time:

  1. The module must be removed, because it is not in theory.
  2. It is better not to use tangents in its pure form, because there is a risk of error.
    So, for the same data by the formula
    phi = (Math.atan2(y2, x2) - Math.atan2(y1, x1);
    we have: fi = arctg (sqrt (-3)) - arctg (sqrt (3)) = -120.
    The paradox is explained by the fact that the tangent has a period of 180, and
    cos (fi + 180) = cos (fi) cos (180) + sin (fi) sin (180) = - cos (fi) .
  • strange, but there is no confusion yet - Khusamov Sukhrob
  • Thank you for your comment. I clarified the answer. - Yuri Negometyanov