2011-09-11 161 views
9

我要实现环境ASIN,ACOS和ATAN这里我只以下的数学工具:逼近反三角函数

  • 正弦
  • 余弦
  • 基本定点运算(浮点数不可用)

我也已经有相当不错的平方根功能。

我可以使用它们来实现合理高效的反三角函数吗?

我不需要太高的精度(反正浮点数的精度非常有限),基本的近似值可以。

我已经半决定去查表,但我想知道是否有一些整洁的选项(这不需要几百行代码来实现基本的数学)。

编辑:

要澄清一些事情:我需要以每秒35帧运行函数每秒数百帧时间。

+0

[三角函数如何工作?](http://stackoverflow.com/questions/345085/how-do-trigonometric-functions-work) –

+0

建议的副本更多关于三角函数如何工作(就像这是关于反三角函数 – Teepeemm

回答

2

你需要一个大的精度arcsin(x)功能?如果否,则可以在N个节点中计算arcsin,并将值保存在内存中。我建议使用行aproximation。如果x = A*x_(N) + (1-A)*x_(N+1)x = A*arcsin(x_(N)) + (1-A)*arcsin(x_(N+1))其中arcsin(x_(N))是已知的。

+0

是的,这是我在OP中讨论的表格查询。没有看到为什么我会在运行时计算出来的原因,我会simoly将值烘焙到程序中,所以实际的计算只是两个值之间的插值。 –

2

您可能想要使用近似值:使用infinite series,直到解决方案足够接近您为止。

例如:
arcsin(z) = Sigma((2n!)/((2^2n)*(n!)^2)*((z^(2n+1))/(2n+1)))其中n在[0,无穷大)

1

也许一些种像牛顿拉普逊智能蛮力。

所以解决ASIN()你最速下降下去了罪()

+0

,你可以从一个小查找表中选择起点来加速计算 –

8

在固定点e环境(S15.16)我成功地使用了CORDIC算法(参见维基百科的一般描述)来计算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) 

事实证明,找到一个有用的关于atan2()的迭代的CORDIC迭代比我想象的更难。下面的网站似乎包含足够详细的说明,同时还讨论了两种替代方法,多项式逼近与查找表:

http://ch.mathworks.com/examples/matlab-fixed-point-designer/615-calculate-fixed-point-arctangent

+0

从wikip edia,CORDIC甚至不使用trig函数(整洁!);我想象你所做的是搜索;给定sin(),cos()似乎牛顿 - 拉夫森或者其他一些会更好? (需要更少的迭代,尽管迭代的代价会有所不同。) – petrelharp

+0

我建议查看CORDIC的原因是因为它只需要定点算术。 CORDIC最常见的用途可能是实施sin/cos,这是我第一次了解它(1987年)。但是也可以用CORDIC来计算其他很多功能,例如atan2。由于我没有任何代码可以用CORDIC来计算atan2,所以我试图找到一个足够详细的网站,以便有人可以在其上实现一个实现。我上面发布的链接是我可以在几分钟内通过搜索引擎找到的最佳网页。 – njuffa

1

将以下代码添加到固定点应该很容易。它使用rational approximation来计算归一化到[0 1)区间的反正切(你可以乘以Pi/2得到真正的反正切)。然后,您可以使用well known identities从反正切中获取弧线/弧线。

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; 
} 

如果你需要更精确,有一个3阶有理函数:

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最大逼近误差º

1

在此提交我的回答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即可获得度数的结果,这也是来自他们的“度数”公式。

-1

只有连续函数可以用多项式近似。并且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 )](如果arcsin的请求角度大于sqrt(1/2)的值,则找到cos(x)。仅适用于x的反正切函数1.arctan(x)= pi/2-arctan(1/x)。