近似反三角函数
Posted
技术标签:
【中文标题】近似反三角函数【英文标题】:Approximating inverse trigonometric functions 【发布时间】:2011-09-11 12:32:38 【问题描述】:我必须在只有以下数学工具的环境中实现 asin、acos 和 atan:
正弦 余弦 基本定点算术(浮点数不可用)我也已经有了相当好的平方根函数。
我可以使用它们来实现相当有效的反三角函数吗?
我不需要太大的精度(无论如何,浮点数的精度非常有限),基本的近似值就可以了。
我已经决定使用表查找,但我想知道是否有更简洁的选项(不需要几百行代码就可以实现基本数学)。
编辑:
搞清楚:我需要以每秒 35 帧的速度每帧运行数百次函数。
【问题讨论】:
How do Trigonometric functions work? 的可能重复项 建议的副本更多地是关于三角函数的工作原理(就像它的标题一样)。这是关于反三角函数的。 【参考方案1】:在定点环境(S15.16)中,我成功地使用了 CORDIC 算法(有关一般描述,请参见 Wikipedia)来计算 atan2(y,x),然后从中导出 asin() 和 acos() - 涉及平方根的已知函数恒等式:
asin(x) = atan2 (x, sqrt ((1.0 + x) * (1.0 - x)))
acos(x) = atan2 (sqrt ((1.0 + x) * (1.0 - x)), x)
事实证明,在 double 上为 atan2() 找到 CORDIC 迭代的有用描述比我想象的要难。以下网站似乎包含足够详细的描述,并且还讨论了两种替代方法,多项式逼近和查找表:
http://ch.mathworks.com/examples/matlab-fixed-point-designer/615-calculate-fixed-point-arctangent
【讨论】:
来自***,CORDIC 甚至不使用三角函数(简洁!);我想你所做的是搜索;给定 sin(), cos() 似乎 Newton-Raphson 或类似的会更好? (需要更少的迭代,尽管迭代的成本会有所不同。) 我建议研究 CORDIC 的原因是因为它只需要定点算术。 CORDIC 最常见的用途可能是实现 sin / cos,这就是我第一次了解它的方式(1987 年)。但是也可以使用 CORDIC 计算很多其他函数,例如 atan2。由于我没有任何代码用于使用 CORDIC 计算 atan2,因此我试图找到一个具有足够详细信息的网站,以便有人可以基于它来实现。我在上面发布的链接是我在几分钟内通过搜索引擎找到的最佳页面。【参考方案2】:arcsin(x)
函数需要较大的精度吗?如果不是,您可以在 N 个节点中计算 arcsin
,并将值保存在内存中。我建议使用线近似。如果x = A*x_(N) + (1-A)*x_(N+1)
则x = A*arcsin(x_(N)) + (1-A)*arcsin(x_(N+1))
其中arcsin(x_(N))
是已知的。
【讨论】:
是的,这就是我在 OP 中所说的表查找。看不出我为什么会在运行时计算出的原因,我会将这些值直接烘焙到程序中,所以实际的 asin 计算只是两个值之间的插值。【参考方案3】:您可能想要使用近似值:使用 infinite series 直到解决方案对您来说足够接近。
例如:
arcsin(z) = Sigma((2n!)/((2^2n)*(n!)^2)*((z^(2n+1))/(2n+1)))
where n in [0,infinity)
【讨论】:
【参考方案4】:http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Expression_as_definite_integrals
您可以使用平方根函数进行数值积分,近似于无限级数:
【讨论】:
我刚刚从***复制了图片。【参考方案5】:将以下代码添加到定点应该很容易。它使用rational approximation 来计算归一化为 [0 1) 区间的反正切(您可以将其乘以 Pi/2 以获得真正的反正切)。然后,您可以使用well known identities 从反正切中获取 arcsin/arccos。
normalized_atan(x) ~ (b x + x^2) / (1 + 2 b x + x^2)
where b = 0.596227
最大误差为0.1620º
#include <stdint.h>
#include <math.h>
// Approximates atan(x) normalized to the [-1,1] range
// with a maximum error of 0.1620 degrees.
float norm_atan( float x )
static const uint32_t sign_mask = 0x80000000;
static const float b = 0.596227f;
// Extract the sign bit
uint32_t ux_s = sign_mask & (uint32_t &)x;
// Calculate the arctangent in the first quadrant
float bx_a = ::fabs( b * x );
float num = bx_a + x * x;
float atan_1q = num / ( 1.f + bx_a + num );
// Restore the sign bit
uint32_t atan_2q = ux_s | (uint32_t &)atan_1q;
return (float &)atan_2q;
// Approximates atan2(y, x) normalized to the [0,4) range
// with a maximum error of 0.1620 degrees
float norm_atan2( float y, float x )
static const uint32_t sign_mask = 0x80000000;
static const float b = 0.596227f;
// Extract the sign bits
uint32_t ux_s = sign_mask & (uint32_t &)x;
uint32_t uy_s = sign_mask & (uint32_t &)y;
// Determine the quadrant offset
float q = (float)( ( ~ux_s & uy_s ) >> 29 | ux_s >> 30 );
// Calculate the arctangent in the first quadrant
float bxy_a = ::fabs( b * x * y );
float num = bxy_a + y * y;
float atan_1q = num / ( x * x + bxy_a + num );
// Translate it to the proper quadrant
uint32_t uatan_2q = (ux_s ^ uy_s) | (uint32_t &)atan_1q;
return q + (float &)uatan_2q;
如果您需要更高的精度,可以使用三阶有理函数:
normalized_atan(x) ~ ( c x + x^2 + x^3) / ( 1 + (c + 1) x + (c + 1) x^2 + x^3)
where c = (1 + sqrt(17)) / 8
最大近似误差为 0.00811º
【讨论】:
【参考方案6】:在这里提交我来自other similar question.的答案
nVidia 有一些很棒的资源,我自己用过,几个例子:acosasinatan2 等等...
这些算法产生足够精确的结果。这是一个直接的 Python 示例,其中粘贴了他们的代码副本:
import math
def nVidia_acos(x):
negate = float(x<0)
x=abs(x)
ret = -0.0187293
ret = ret * x
ret = ret + 0.0742610
ret = ret * x
ret = ret - 0.2121144
ret = ret * x
ret = ret + 1.5707288
ret = ret * math.sqrt(1.0-x)
ret = ret - 2 * negate * ret
return negate * 3.14159265358979 + ret
以下是比较结果:
nVidia_acos(0.5) result: 1.0471513828611643
math.acos(0.5) result: 1.0471975511965976
这很接近!乘以 57.29577951 得到度数的结果,这也是他们的“度数”公式。
【讨论】:
【参考方案7】:也许是某种聪明的蛮力,比如牛顿拉普森。
因此,为了解决 asin(),你需要在 sin() 上进行最陡峭的下降
【讨论】:
您可以从一个小的查找表中选择起点以加快计算速度。【参考方案8】:使用多项式近似。最小二乘拟合最简单(Microsoft Excel 有),切比雪夫近似更准确。
这个问题之前已经讨论过:How do Trigonometric functions work?
【讨论】:
【参考方案9】:只有连续函数才能被多项式逼近。并且 arcsin(x) 在点 x=1.same arccos(x) 是不连续的。但是在这种情况下将范围缩小到区间 1,sqrt(1/2) 可以避免这种情况。我们有 arcsin(x)=pi/2- arccos(x),arccos(x)=pi/2-arcsin(x)。您可以使用 matlab 进行极小极大近似。仅在 [0,sqrt(1/2) 范围内近似)](如果该 arcsin 的角度请求大于 sqrt(1/2) 仅针对 x
【讨论】:
以上是关于近似反三角函数的主要内容,如果未能解决你的问题,请参考以下文章