( J'ai déjà testé cette approche et je me souviens qu'elle a fonctionné correctement, mais je ne l'ai pas testée spécifiquement pour cette question. )
Autant que je sache, les deux et peuvent souffrir d'une annulation catastrophique s'ils sont presque parallèles / perpendiculaires - atan2 ne peut pas vous donner une bonne précision si l'une des entrées est désactivée.∥v1×v2∥v1⋅v2
Commencez par reformuler le problème en trouvant l'angle d'un triangle avec des longueurs de côté,et(ils sont tous calculés avec précision en arithmétique à virgule flottante). Il existe une variante bien connue de la formule de Heron en raison de Kahan ( zone de calcul et angles d'un triangle en forme d'aiguille ), qui vous permet de calculer la zone et l'angle (entre et ) d'un triangle spécifié par ses longueurs de côté, et le faire numériquement de manière stable. Étant donné que la réduction de ce sous-problème est également exacte, cette approche devrait fonctionner pour des entrées arbitraires.a=|v1|b=|v2|c=|v1−v2|ab
Citant cet article (voir p. 3), en supposant ,
Toutes les parenthèses ici sont placées avec soin, et elles comptent; si vous vous retrouvez à prendre la racine carrée d'un nombre négatif, les longueurs latérales d'entrée ne sont pas les longueurs latérales d'un triangle.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)−−−−−−−−−−−−−−−−−−−−√)
Le document de Kahan explique comment cela fonctionne, y compris des exemples de valeurs pour lesquelles d'autres formules échouent. Votre première formule pour est à la page 4.αC′′
La principale raison pour laquelle je suggère la formule de Heron de Kahan est parce qu'elle fait une très belle primitive — beaucoup de questions de géométrie planaire potentiellement délicates peuvent être réduites à trouver l'aire / l'angle d'un triangle arbitraire, donc si vous pouvez réduire votre problème à cela, il y a une belle formule stable pour cela, et il n'est pas nécessaire de trouver quelque chose par vous-même.
Modifier Suite au commentaire de Stefano, j'ai fait un tracé d'erreur relative pour , ( code ). Les deux lignes sont les erreurs relatives pour et , suivant l'axe horizontal. Il semble que ça marche.
v1=(1,0)v2=(cosθ,sinθ)θ=ϵθ=π/2−ϵϵ