( Я проверял этот подход раньше, и я помню, что он работал правильно, но я не проверял его специально для этого вопроса. )
Насколько я могу судить, оба и может пострадать от катастрофической отмены, если они почти параллельны / перпендикулярны - atan2 не может дать вам хорошую точность, если любой из входов отключен.∥v1×v2∥v1⋅v2
Начните с переформулировки задачи как нахождения угла треугольника с длинами сторон,и(все они точно рассчитаны в арифметике с плавающей запятой). Существует хорошо известный вариант формулы Герона из-за Кахана ( Miscalculation Area and Angles of Needle-подобные треугольники ), который позволяет вычислять площадь и угол (между и ) треугольника, заданного длиной его стороны, и делать это численно стабильно. Поскольку приведение к этой подзадаче также является точным, этот подход должен работать для произвольных входных данных.a=|v1|b=|v2|c=|v1−v2|ab
Цитирование из этой статьи (см. Стр.3), предполагая, что ,
Все круглые скобки здесь размещены аккуратно, и они имеют значение; если вы обнаружите, что берете квадратный корень из отрицательного числа, длина входной стороны не равна длине стороны треугольника.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)−−−−−−−−−−−−−−−−−−−−√)
Существует объяснение того, как это работает, включая примеры значений, для которых другие формулы не работают, в статье Кахана. Ваша первая формула для - это на странице 4.αC′′
Основная причина, по которой я предлагаю формулу Керона Кахана, состоит в том, что она делает очень хороший примитив - множество потенциально хитрых вопросов плоской геометрии может быть сведено к нахождению площади / угла произвольного треугольника, так что если вы можете свести свою проблему к этому, хорошая стабильная формула для этого, и нет необходимости придумывать что-то самостоятельно.
Редактировать После комментария Стефано я сделал график относительной ошибки для , ( код ). Две линии - это относительные ошибки для и , идущих вдоль горизонтальной оси. Кажется, это работает.
v1=(1,0)v2=(cosθ,sinθ)θ=ϵθ=π/2−ϵϵ