Страницы

Поиск по вопросам

вторник, 4 июня 2019 г.

Как составить правильную формулу расчета угла между векторами?

Есть стандартная формула расчета угла между векторами:

И вот есть два вектора, угол между которыми никак не вычислить, так как правая часть уравнения меньше -1. Вот эти вектора:
var x1 = -0.045797169475341334, y1 = -0.9989507591808752; var x2 = 0.04579716947534099, y2 = 0.9989507591808753;
В итоге, выражение:
(x1 * x2 + y1 * y2) / Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2))
дает результат: -1.0000000000000002
И если взять арккосинус этого числа, то будет NaN, что и понятно, так как он определен на промежутке от -1 до 1
Как мне скорректировать формулу, чтобы этой ошибки не было?


Ответ

Варианта решения проблемы имеются как минимум 2. Оба они требует, чтобы оба вектора имели ненулевую длину, но для вектора с нулевой длиной сама постановка вопроса о каком-либо угле не вполне корректна, и как Вам обрабатывать такую ситуацию, Вам должно быть виднее.
1) вычисляем промежуточное значение:
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))
Проверяем на попадание r в диапазон [-1, 1]:
if (r < -1) r = -1; if (r > 1) r = 1;
Вычисляем арккосинус. Так убираются ошибки округления, "выбивающие" r из диапазона.
2) Пользуемся функцией atan2
phi = Math.atan2(y2, x2) - Math.atan2(y1, x1);
При необходимости, приводим полученный угол в нужный диапазон (0-180град или -90 - +90)

Я бы написал так:
if (phi < 0) phi += Math.PI * 2; if (phi > Math.Pi) phi -= Math.PI;

Оба алгоритма выдадут ошибку при x1=y1=0 или x2=y2=0, о чем написано выше. Вариант с atan2, имхо предпочтительней, т.к., например при x1=y1=1e+10, x2=y2=1e-10 точность вычислений по первому варианту будет околонулевой (e10 взято просто для примера, м.б. нужно существенно больше).

Математические функции указаны для Java, для .NET названия содержат заглавную букву: Math.Sqrt, Math.Atan2 и т. д.

Комментариев нет:

Отправить комментарий