逼近反三角函数

时间:2011-09-11 12:32:39

标签: algorithm math trigonometry

我必须在我只有以下数学工具的环境中实现asin,acos和atan:

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

我也已经有了相当不错的平方根函数。

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

我不需要太大的精度(浮点数无论如何都具有非常有限的精度),基本近似就可以了。

我已经决定使用表查找,但我想知道是否有一些更简洁的选项(不需要几百行代码来实现基本的数学运算)。

编辑:

要清理:我需要每帧以每秒35帧的速度运行该功能数百次。

9 个答案:

答案 0 :(得分:8)

在定点环境(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)

事实证明,在double上找到atan2()的CORDIC迭代的有用描述比我想象的要难。以下网站似乎包含足够详细的描述,并讨论了两种替代方法,多项式逼近和查找表:

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

arcsin(x)函数需要大的精度吗?如果不是,您可以在N个节点中计算arcsin,并将值保留在内存中。我建议使用line aproximation。如果x = A*x_(N) + (1-A)*x_(N+1),则x = A*arcsin(x_(N)) + (1-A)*arcsin(x_(N+1))知道arcsin(x_(N))

答案 3 :(得分:1)

也许像牛顿拉夫森这样的智能蛮力。

因此,为了解决asin(),你会在sin()

上进行最陡的下降

答案 4 :(得分:1)

Expression as definite integrals

http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Expression_as_definite_integrals

您可以使用平方根函数以数字方式进行整合,近似为无穷级数:

Infinite series

答案 5 :(得分:1)

将以下代码添加到定点应该很容易。它使用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 :(得分:1)

在此提交我的回答other similar question.

nVidia有一些我用于自己用途的优秀资源,很少有例子:acos asin atan2等等......

这些算法产生足够精确的结果。这是一个直接的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得到度数的结果,也来自他们的&#34;度&#34;式。

答案 7 :(得分:0)

使用多项式近似。最小二乘拟合最简单(Microsoft Excel有),Chebyshev逼近更准确。

此问题已在之前介绍过:How do Trigonometric functions work?

答案 8 :(得分:-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(1/2)中的近似)](如果arcsin的请求的角度大于sqrt(1/2),则找到cos(x).arctangent函数仅适用于x <1.arctan(x)= pi / 2-arctan(1 / x)。 / p>