Computing the angle between two vectors

栏目: IT技术 · 发布时间: 4年前

内容简介:Given two vectors in three dimensions, what is the most accurate way to compute the angle between them? I have seen several different approaches to this problem recently in the wild, and although I knew some of them had potential issues, I wasn’t sure just

Introduction

Given two vectors in three dimensions, what is the most accurate way to compute the angle between them? I have seen several different approaches to this problem recently in the wild, and although I knew some of them had potential issues, I wasn’t sure just how bad things might get in practice, nor which alternative was best as a replacement.

To make the setup more precise, let’s assume that we are given two non-zero input vectors Computing the angle between two vectors , represented exactly by their double-precision coordinates, and we desire a function that returns a double-precision value that most closely approximates the angle Computing the angle between two vectors between the vectors, with all intermediate computation also done in double precision.

Kahan’s Mangled Angles

William Kahan discusses three formulas in the “Mangled Angles” section of the paper linked below. The first is the “usual” dot product formula:

Computing the angle between two vectors

with the following C++ implementation, which as Kahan points out requires clamping the double-precision dot product to the interval Computing the angle between two vectors to avoid a NaN result for some vectors that are nearly parallel:

double angle(const Vector& u, const Vector& v)
{
    return std::acos(std::min(1.0, std::max(-1.0,
        dot(u, v) / (norm(u) * norm(v)))));
}

Kahan subsequently describes another formula using the cross product:

with the following implementation:

double angle(const Vector& u, const Vector& v)
{
    double angle = std::asin(std::min(1.0,
        norm(cross(u, v)) / (norm(u) * norm(v))));
    if (dot(u, v) < 0)
    {
        angle = 3.141592653589793 - angle;
    }
    return angle;
}

Interestingly, Kahan does not mention that this formula also requires clamping the asin argument to the interval Computing the angle between two vectors ; following is an explicit example of inputs demonstrating the potential problem:

Computing the angle between two vectors

Computing the angle between two vectors

Finally, despite referring to the above formula as “the best known in three dimensions,” Kahan finishes with the following “better formula less well known than it deserves”:

Computing the angle between two vectors

with the following implementation:

double angle(const Vector& u, const Vector& v)
{
    double nu = norm(u);
    double nv = norm(v);
    return 2 * std::atan2(norm(nv * u - nu * v), norm(nv * u + nu * v));
}

That’s a lot of square roots. I didn’t focus on performance here, but it would be an interesting follow-on analysis to compare the speed of each of these formulas.

Other approaches are possible; following is the formula that I thought was the most accurate, before reading Kahan’s paper:

Computing the angle between two vectors

double angle(const Vector& u, const Vector& v)
{
    return std::atan2(norm(cross(u, v)), dot(u, v));
}

This has the added benefit of involving just a single square root. This is the formula that I used to compute the “true” angle between vectors to compare errors, usingarbitrary-precision rational arithmetic to compute the square root (actually, a reciprocal square root, which is slightly easier) and arctangent. All of the source code is available on GitHub .

And finally, the approach that I saw most recently that motivated this post, using the Law of cosines :

Computing the angle between two vectors

double angle(const Vector& u, const Vector& v)
{
    double u2 = u.x * u.x + u.y * u.y + u.z * u.z;
    double v2 = v.x * v.x + v.y * v.y + v.z * v.z;
    Vector d = u - v;
    return std::acos(std::min(1.0, std::max(-1.0,
        (u2 + v2 - (d.x * d.x + d.y * d.y + d.z * d.z)) /
        (2 * std::sqrt(u2) * std::sqrt(v2)))));
}

Results

The relative accuracy of each formula depends on the magnitude of the angle between the input vectors. The following figure shows this comparison for angles near 0 (i.e., nearly parallel vectors), near Computing the angle between two vectors , near Computing the angle between two vectors (i.e., nearly orthogonal vectors), and near (i.e., nearly “anti-parallel” vectors).

Computing the angle between two vectors

Absolute error between computed and exact rounded double-precision angle, vs. true/exact angle.

The x -axis indicates an offset from the “true” angle between the input vectors, computed to roughly 200-bit accuracy. The y -axis indicates the error in the double-precision output, compared against the true angle also rounded to double-precision . The points hugging the bottom of each figure are my poor man’s attempt at indicating zero error (note that these are on a log-log scale), i.e., the 64-bit double-precision output matched the corresponding value rounded from the 200-bit true angle. (In many ways this figure feels like a failure of visual display of quantitative information, and I’m not sure how best to improve it.)

So what’s the takeaway? If you don’t care about absolute errors smaller than a few dozen nanoradians, then it doesn’t really matter which formula you use. And if you do care about errors– and angles– smaller than that, then be sure that your inputs are accurate in the first place. For example, did you normalize your “real” input vectors to unit length first, and if so, how much error did you unintentionally incur as a result? We can construct very small angles between vectors if we restrict to “nice” two-dimensional inputs like Computing the angle between two vectors and Computing the angle between two vectors . But it’s an interesting exercise to see how difficult it is to construct vectors “in general position” (e.g., randomly rotated) with a prescribed small angle between them.

As expected, the two arccosine formulas behave poorly for nearly parallel/anti-parallel vectors, and as Kahan describes, the arcsine formula behaves poorly for nearly orthogonal vectors. The two arctangent formulas are the most consistently accurate, and when the one mentioned by Kahan is better, it’s typically much better.

Reference:

  1. Kahan, W., How Futile are Mindless Assessments of Roundoff in Floating-Point Computation? [ PDF ]

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

世界因你不同

世界因你不同

李开复、范海涛 / 中信出版社 / 2010 / 29.8

这是李开复唯一的一本自传,字里行间,是岁月流逝中沉淀下来的宝贵的人生智慧和职场经验。捣蛋的“小皇帝”,11岁的“留学生”,奥巴马的大学同学,26岁的副教授,33岁的苹果副总裁,谷歌中国的创始人,他有着太多传奇的经历,为了他,两家最大的IT公司对簿公堂。而他的每一次人生选择,都是一次成功的自我超越。   透过这本自传,李开复真诚讲述了他鲜为人知的成长史、风雨兼程的成功史和烛照人生的心灵史,也首次全面......一起来看看 《世界因你不同》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换