Como devo lidar com ARCTAN (e impedir a divisão por zero), sem a conveniência de arctan2 (n, m) estar disponível?

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

Pergunta

Eu tento para determinar o ângulo de uma (n,m) ponto para (0,0). Sem estar disponível arctan2, eu estou correndo para o problema que m pode ser 0, o que leva a uma possível divisão por zero.

O que seria uma solução elegante, correta para abordar esta questão?

Foi útil?

Solução

não usar os quadrantes convencionais, usar os pontos de ramificação definida pelas linhas y = +/- x, e utilizar o primeiro de dois passos de um CORDIC-como algoritmo (por exemplo, coordenadas de rotação por um ângulo conhecido e acompanhar O quanto você rodada):

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
}

a razão para fazê-lo desta maneira é que atan () pode ser mais provável que seja preciso para rácios de y / x entre -1 e +1, do que para proporções maiores do que 1. (embora um algoritmo bom atan () faria reconhecer isso e tomar a recíproca)

Outras dicas

Se atan2 não está disponível, você tem que verificar a dividir por condição zero e todos os outros casos especiais em seu código. Fácil assim. A entrada da Wikipedia sobre atan2 tem todas as condições de que necessita.

Se os seus suportes de hardware alvo dividir por zero exceções para operações de ponto flutuante você tem outra opção:

Instale um manipulador de baixo nível que verifica a causa de exceção e se happends ser uma solução divisão atan o problema. Isso fará com que o seu atan2 mais rápido se as exceções são raras, mas requer baixo nível de ajustes e não é portátil.

Implementar a arctan(n, m) padrão, utilizando série de Taylor e faça o seguinte antes de arctan computação:

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

Mais alguns truques:

1) se |m| < |n|, m, n swap e arctan então compute. Finalmente subtrair o resultado da Pi/2

2) se |m| está próximo de |n|, arctan computar o ângulo de meia usando a fórmula semi-ângulo arctan(x) = 2*arctan(x/(1+sqrt(1+x*x))), de outra forma, pois tais valores, converge ARCTAN muito lentamente

Defina a sua versão do arctan2. Um exemplo em C como uma macro:

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

É claro, você pode elaborar para encontrar o quadrante dependendo sinais de n e m.

Eu acredito que esta é uma implementação correta de atan2 usando atan (não lidar com infinitos, embora):

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);
    }
}

equipamento de teste:

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);
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top