余切函数的实现是否比返回1.0 / tan(x)更稳定;?
答案 0 :(得分:37)
cot(x) = cos(x)/sin(x)
应该比cot(x) = 1/tan(x)
更接近π/ 2的数值稳定性。您可以在拥有它的平台上使用sincos
有效地实现它。
另一种可能性是cot(x) = tan(M_PI_2 - x)
。这应该比上面的更快(即使sincos
可用),但它也可能不太准确,因为M_PI_2
当然只是超越数π/ 2的近似值,所以区别M_PI_2 - x
对于double
尾数的全宽度不准确 - 事实上,如果你运气不好,它可能只有几个有意义的位。
答案 1 :(得分:2)
TL; DR号
作为经验法则,在寻找误差源时,应首先关注加法和减法,这可能会导致减法抵消的问题。除了增加舍入误差外,乘法和除法通常对精度无害,但可能会由于中间计算中的上溢和下溢而引起问题。
没有机器号x
足够接近π/ 2的倍数而导致tan(x)
溢出,因此tan(x)
定义明确,对于所有浮点编码都是有限的cot(x) = 1.0 / tan(x)
也是任何IEEE-754浮点格式,并且通过扩展也是如此。
通过使用所有数字float
编码进行穷举测试很容易证明这一点,因为使用double
进行穷举测试是不可行的,但可能与当今最大的超级计算机除外。
使用精确实现tan()
且最大误差为〜= 0.5 ulp的数学库,我们发现计算cot(x) = 1.0 / tan(x)
产生的最大误差小于1.5 ulp,与tan()
本身相比,附加误差是由除法的舍入误差引起的。
使用float
对所有cot(x) = cos(x) / sin(x)
值重复此详尽测试,其中计算出sin()
和cos()
的最大误差为〜= 0.5 ulp,我们发现cot()
中的最大错误小于2.0 ulps,因此稍大。可以通过使用三个误差源而不是上一个公式中的两个误差源来轻松解释这一点。
最后,cot(x) = tan (M_PI_2 - x)
遇到前面提到的x
在M_PI_2附近时的减法消除问题,以及M_PI_2 - x == M_PI_2
在有限精度浮点算法中的问题,当{ {1}}的大小足够小。这可能会导致非常大的错误,导致结果中没有有效位。
答案 2 :(得分:1)
如果考虑两个向量(v
和w
)之间的夹角,还可以按如下方式获得余切(使用Eigen::Vector3d):
inline double cot(Eigen::Vector3d v, Eigen::Vector3d w) {
return( v.dot(w) / (v.cross(w).norm()) );
};
使用θv
和w
之间的角度,上面的函数是正确的,因为: