Как я должен заняться ARCTAN (и предотвратить деление на ноль) без удобства ARCTAN2 (n, m) быть доступным?

StackOverflow https://stackoverflow.com/questions/639283

Вопрос

Я пытаюсь определить угол от точки (n,m) до (0,0). Поскольку arctan2 недоступно, я сталкиваюсь с проблемой, что m может быть 0, что приводит к возможному делению на ноль.

Что было бы элегантным, правильным решением для решения этой проблемы?

Это было полезно?

Решение

не используйте обычные квадранты, используйте точки ветвления, определенные линиями y = +/- x, и используйте первые два шага CORDIC-подобного алгоритма (например, вращайте координаты на известный угол и отслеживайте сколько вы повернули):

function atan2_substitute(x,y)
{
   double angle = 0;
   if (x < y)
   { angle = M_PI; x = -x; y = -y; }
   // this guarantees that the angle is between -135 and +45 degrees

   if (x < -y)
   {
     angle -= M_PI/2; tmp = x; x = -y; y = tmp;
   }
   // this guarantees that the angle is between -45 and +45

   angle += atan(y/x);

   if (angle > M_PI)
      angle -= 2*M_PI;
   // fails at 0,0; otherwise is accurate over the entire plane
}

причина для этого заключается в том, что atan () может быть более точным для соотношений y / x между -1 и +1, чем для соотношений больше 1. (хотя хороший алгоритм atan () будет признать это и принять ответный ответ)

Другие советы

Если atan2 недоступен, вы должны проверить условие деления на ноль и все другие особые случаи в вашем коде. Легко как то. Запись в википедии на atan2 имеет все необходимые условия.

Если целевое оборудование поддерживает деление на ноль исключений для операций с плавающей запятой, у вас есть другая опция:

Установите низкоуровневый обработчик, который проверяет причину исключения и, если он является подразделением atan, устраняет проблему. Это сделает ваш atan2 быстрее, если исключения редки, но он требует низкоуровневой обработки и не переносим.

Реализуйте стандарт arctan(n, m), используя ряды Тейлора, и выполните следующие действия перед вычислением arctan:

if (m == 0) {
    if (n < 0) return Pi;
    return 0;
}

Еще несколько хитростей:

1) если |m| < |n|, поменять местами m, n, а затем вычислить arctan. Наконец, вычтите результат из Pi/2

2) если |m| близко к |n|, вычислите арктан для половинного угла, используя формулу половинного угла arctan(x) = 2*arctan(x/(1+sqrt(1+x*x))) в противном случае для таких значений arctan сходится очень медленно

Определите свою версию arctan2. Пример в C как макрос:

#define atan2(n,m)   (m)==0 ? M_PI_2 : atan((n)/(m))

Конечно, вы можете разработать поиск квадранта в зависимости от признаков n и m.

Я считаю, что это правильная реализация atan2 с использованием atan (хотя и не обрабатывает бесконечности):

float my_atan2(float y, float x)
{
    if(x == 0) // might also want to use fabs(x) < 1e-6 or something like that
    {
        if(y > 0)
            return M_PI_2;
        else
            return -M_PI_2;
    }
    else if(x > 0)
    {
        return atan(y/x);
    }
    else 
    {
        // x < 0                                                                                
        if(y > 0)
            return M_PI + atan(y/x);
        else
            return -M_PI + atan(y/x);
    }
}

Испытательный жгут:

int main()
{
    for(int i = -360; i <= 360; i++)
    {
        float x = cos(i / 180.0 * M_PI);
        float y = sin(i / 180.0 * M_PI);


        float good = atan2(y, x);
        float mine = my_atan2(y, x);


        if(fabs(good - mine) > 1e-6)
        {
            printf("%d %f %f %f %f\n", i, x, y, good, mine);
        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top