我必须在我只有以下数学工具的环境中实现asin,acos和atan:
我也已经有了相当不错的平方根函数。
我可以使用它们来实现合理有效的反三角函数吗?
我不需要太大的精度(浮点数无论如何都具有非常有限的精度),基本近似就可以了。
我已经决定使用表查找,但我想知道是否有一些更简洁的选项(不需要几百行代码来实现基本的数学运算)。
编辑:
要清理:我需要每帧以每秒35帧的速度运行该功能数百次。
答案 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)
http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Expression_as_definite_integrals
您可以使用平方根函数以数字方式进行整合,近似为无穷级数:
答案 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>