( Eu testei essa abordagem antes e lembro que funcionou corretamente, mas não a testei especificamente para esta pergunta. )
Até onde eu sei, tanto e podem sofrer um cancelamento catastrófico se forem quase paralelos / perpendiculares - o atan2 não poderá fornecer boa precisão se uma das entradas estiver desativada.∥v1×v2∥v1⋅v2
Comece reformulando o problema ao encontrar o ângulo de um triângulo com comprimentos laterais,e(todos são calculados com precisão na aritmética de ponto flutuante). Há uma variante bem conhecida de fórmula de Heron devido a Kahan ( Área miscalculating e ângulos de uma forma de agulha do triângulo ), que permite calcular a área e o ângulo (entre e ) de um triângulo especificado pelos seus comprimentos laterais, e faça isso numericamente de maneira estável. Como a redução desse subproblema também é precisa, essa abordagem deve funcionar com entradas arbitrárias.a=|v1|b=|v2|c=|v1−v2|ab
Citando esse artigo (consulte a p.3), assumindo ,
Todos os parênteses aqui são colocados com cuidado e são importantes; se você encontrar a raiz quadrada de um número negativo, os comprimentos dos lados de entrada não serão os comprimentos laterais de um triângulo.a≥b
μ=⎧⎩⎨c−(a−b),b−(a−c),invalid triangle,if b≥c≥0,if c>b≥0,otherwise
angle=2arctan(((a−b)+c)μ(a+(b+c))((a−c)+b)−−−−−−−−−−−−−−−−−−−−√)
Há uma explicação de como isso funciona, incluindo exemplos de valores para os quais outras fórmulas falham, no artigo de Kahan. Sua primeira fórmula para é na página 4.αC′′
A principal razão pela qual sugiro a fórmula de Kahan Heron é porque ela é uma primitiva muito boa - muitas questões de geometria planar potencialmente complicadas podem ser reduzidas para encontrar a área / ângulo de um triângulo arbitrário; portanto, se você pode reduzir seu problema a isso, existe uma boa fórmula estável para isso, e não há necessidade de criar algo por conta própria.
Editar Após o comentário de Stefano, fiz um gráfico de erro relativo para , ( código ). As duas linhas são os erros relativos de e , ao longo do eixo horizontal. Parece que funciona.
v1=(1,0)v2=(cosθ,sinθ)θ=ϵθ=π/2−ϵϵ