( He probado este enfoque antes, y recuerdo que funcionó correctamente, pero no lo he probado específicamente para esta pregunta ) .
Por lo que puedo decir, ambos y puede sufrir una cancelación catastrófica si son casi paralelas / perpendiculares; atan2 no puede darle una buena precisión si alguna de las entradas está desactivada.∥v1×v2∥v1⋅v2
Comience reformulando el problema como encontrar el ángulo de un triángulo con longitudes laterales,y(todos estos se calculan con precisión en aritmética de coma flotante). Hay una variante bien conocida de la fórmula de Heron debido a Kahan ( Area calcular mal y ángulos de una aguja similar a Triangle ), que le permite calcular el área y el ángulo (entre y ) de un triángulo especificado por sus longitudes de los lados, y hacerlo numéricamente estable. Debido a que la reducción a este subproblema también es precisa, este enfoque debería funcionar para entradas arbitrarias.a=|v1|b=|v2|c=|v1−v2|ab
Citando de ese documento (ver p.3), suponiendo ,
Todos los paréntesis aquí se colocan cuidadosamente, y son importantes; si te encuentras tomando la raíz cuadrada de un número negativo, las longitudes laterales de entrada no son las longitudes laterales de un 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)−−−−−−−−−−−−−−−−−−−−√)
Hay una explicación de cómo funciona esto, incluidos ejemplos de valores para los que fallan otras fórmulas, en el artículo de Kahan. Su primera fórmula para es en la página 4.αC′′
La razón principal por la que sugiero la fórmula de Kahan Heron es porque es una primitiva muy agradable: muchas preguntas de geometría plana potencialmente complicadas se pueden reducir a encontrar el área / ángulo de un triángulo arbitrario, por lo que si puede reducir su problema a eso, hay es una fórmula estable y agradable, y no hay necesidad de inventar algo por su cuenta.
Editar Siguiendo el comentario de Stefano, hice un diagrama de error relativo para , ( código ). Las dos líneas son los errores relativos para y , van a lo largo del eje horizontal. Parece que funciona.
v1=(1,0)v2=(cosθ,sinθ)θ=ϵθ=π/2−ϵϵ