Java中将角度标准化为+/-π弧度的标准方法

16

在Java中,是否有库函数或已知的快速高效方法将角度归一化为+/-π,例如在添加两个角度时?

我现在拥有的代码(基于这个答案),基本上就是下面的代码...

private static final double TWO_PI = 2 * Math.PI;

double normalize(double theta) {
    double normalized = theta % TWO_PI;
    normalized = (normalized + TWO_PI) % TWO_PI;
    return normalized <= Math.PI ? normalized : normalized - TWO_PI;
}

...但似乎有些复杂,而且在性能方面,我对取模运算符并不感到兴奋。(请注意,我不能保证theta不是一些相对较大的数字,因此我认为没有纯加法/减法解决方案而无需循环。我实际上不知道手动编写的循环可能与%相比如何。)

是否有经过良好测试和优化的库函数可以使用,或者有更好的算法,还是这是最好的选择?


你是想四舍五入到最接近的90度(半π)还是最接近的180度(整π)? - Kon
@Kon 180度/完整的π。 - David Moles
可能是保持角度在-179到180度之间的简单方法的重复问题。 - Stefano Sanfilippo
@StefanoSanfilippo 我已经阅读了那个答案,但考虑到大多数 Java 数学/几何库都是以弧度为单位的,因此使用弧度而不是角度似乎有更好的答案。 - David Moles
实际上,@CupawnTae提供了一个库函数答案 - David Moles
2个回答

19

Apache Commons提供了一个:

http://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/org/apache/commons/math3/util/MathUtils.html#normalizeAngle(double, double)

将角度规范化为介于 -π 和 +π 之间

a = MathUtils.normalizeAngle(a, 0.0);

查看源代码,也可以用以下代码实现(他们使用自己的FastMath.floor,但如果您想不使用外部库来完成,则可以使用此代码):

theta - TWO_PI * Math.floor((theta + Math.PI) / TWO_PI)

源代码在这里:https://github.com/apache/commons-math/blob/53ec46ba272e23c0c96ada42f26f4e70e96f3115/src/main/java/org/apache/commons/math4/util/MathUtils.java#L107


给未来的读者注意:这个方法刚刚(2017年6月)从最新的commons-math 4.x代码库中被删除。如果您使用此后的版本,您将需要改用 commons-numbers(一旦发布)。当前版本是:

a = PlaneAngleRadians.normalizeBetweenMinusPiAndPi(a);
或者
a = PlaneAngleRadians.normalize(a, 0.0);

17

只有一种百分之百可靠的方法:

public static double normalizeAngle(double angle) {
    return Math.atan2(Math.sin(angle), Math.cos(angle));
}   

其他一切都是人们试图过于聪明而失败。


10
这种方法可能更准确(我认为它提供了一个半开区间,而不像“MathUtils”),但速度也慢大约40倍(在我的15英寸MBP上中位数约为190纳秒,而使用“MathUtils”则为4.9纳秒),这对于某些应用程序可能更重要。 - David Moles
我同意,atan2可能会比较慢。但它可以处理NaN和±Infinity的情况。 - cohadar
MathUtils 也处理这些 -- 它返回 NaN 作为所有三个值的结果,就像 atan2 一样。 - David Moles
1
这个方法在某些角度会产生奇怪的伪影。虽然理论上它是可行的,但我从未能够使其百分之百地可靠运行。 - Martin
2
万无一失?也许吧,我不确定。但是使用三个三角函数来将算术值限制在算术范围内似乎对我来说有些过度了。几个while()语句加上一个单独的if()语句处理边缘情况同样安全。 - Bogdan Stăncescu
结果的范围从-PI到+PI,我希望得到0到2xPI的值。 - Noman_1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接