罪和cos都很慢,还有其他选择吗?

时间:2011-05-23 00:50:13

标签: c++ math

我的游戏需要移动一定的角度。为此,我通过sin和cos得到角度的矢量。不幸的是,罪和cos是我的瓶颈。我相信我不需要这么精确。 C sin&是否有替代品? cos和查找表非常精确但非常快?

我找到了这个:

float Skeleton::fastSin( float x )
{
    const float B = 4.0f/pi;
    const float C = -4.0f/(pi*pi);

    float y = B * x + C * x * abs(x);

    const float P = 0.225f;

    return P * (y * abs(y) - y) + y; 
}

不幸的是,这似乎不起作用。当我使用这个罪而不是C罪时,我会得到明显不同的行为。

由于

8 个答案:

答案 0 :(得分:8)

查找表是标准解决方案。你也可以使用两个查找表进行度数,一个使用十进制度数并利用sin(A + B)= sin(a)cos(b)+ cos(A)sin(b)

答案 1 :(得分:7)

对于fastSin(),您应该查看其文档,了解它的有效范围。您为游戏使用的单位可能太大或太小,并且缩放它们以适应该功能的预期范围可以使其更好地工作。

编辑:

其他人提到通过减去PI来使其进入所需范围,但显然有一个名为fmod的函数用于对浮点数/双精度进行模数除法,所以这应该这样做:

#include <iostream>
#include <cmath>

float fastSin( float x ){
    x = fmod(x + M_PI, M_PI * 2) - M_PI; // restrict x so that -M_PI < x < M_PI
    const float B = 4.0f/M_PI;
    const float C = -4.0f/(M_PI*M_PI);

    float y = B * x + C * x * std::abs(x);

    const float P = 0.225f;

    return P * (y * std::abs(y) - y) + y; 
}

int main() {
    std::cout << fastSin(100.0) << '\n' << std::sin(100.0) << std::endl;
}

我不知道fmod有多贵,所以我接下来会尝试快速基准。

基准测试结果

我使用-O2编译了这个并使用Unix time程序运行结果:

int main() {
    float a = 0;
    for(int i = 0; i < REPETITIONS; i++) {
        a += sin(i); // or fastSin(i);
    }
    std::cout << a << std::endl;
}

结果是sin慢了约1.8倍(如果fastSin需要5秒,sin需要9)。准确性似乎也很不错。

如果您选择这条路线,请确保在(gcc中的-O2)上进行优化编译。

答案 2 :(得分:6)

我知道这已经是一个古老的话题,但对于有相同问题的人来说,这是一个小贴士。

在2D和3D旋转中,很多时候,所有矢量都以固定的角度旋转。而不是在循环的每个循环中调用cos()sin(),而是在循环之前创建变量,其中包含cos(angle)sin(angle)的值。您可以在循环中使用此变量。这样只需要调用一次函数。

答案 3 :(得分:3)

您可以计算256个值的表S,从sin(0)到sin(2 * pi)。然后,为了选择sin(x),在[0,2 * pi]中返回x,你可以从表中选择2个值S [a],S [b],例如&lt; x&lt;湾从这里,线性插值,你应该有一个公平的近似值

  • 内存节省技巧:你实际上只需要从[0,pi / 2]存储,并使用sin(x)的对称性
  • 增强技巧:线性插值可能是一个问题,因为非光滑的导数,人类的眼睛善于发现动画和图形中的这些故障。然后使用三次插值。

答案 4 :(得分:3)

如果您将fastSin中的返回值改为

return (1-P)*y + P*(y*abs(y))

并将y重写为(对于x> 0)

y = 4*x*(pi-x)/pi*pi

你可以看到y是选择的sin(x)的抛物线一阶近似值,因此它通过(0,0),(pi / 2,1)和(pi,0)。 y*abs(y)是一个“修正术语”,它也会通过这些点。

这种形式的整体近似函数保证函数(1-P)* y + P * y * y也将通过(0,0),(pi / 2,1)和(pi,0)。

一个问题是“如何选择P?”。就个人而言,我会选择在0,pi / 2间隔内产生最小RMS误差的P. (我不确定这个 P是如何被选中的)

http://texify.com/img/%5CLARGE%5C%21E%3D%20%5Cint_0%5E%7B%5Cpi/2%7D%20%5B%20%281-p%29%20y%20%2B%20p%20y%5E2%20-%5Csin%28x%29%20%5D%5E2%20%5C%3Bdx.gif

尽量减少这个问题。 P给出

http://texify.com/img/%5CLARGE%5C%21%5Cfrac%7B%5Cpartial%20E%7D%7B%5Cpartial%20p%20%7D%20%3D%202%20%5Cint_0%5E%7B%5Cpi/2%7D%20%28y%5E2-y%29%20%5B%20%281-p%29%20y%20%2B%20p%20y%5E2%20-%5Csin%28x%29%20%5D%20%5C%3Bdx%20%3D0.gif

这可以重新安排并解决p

http://texify.com/img/%5CLARGE%5C%21p%20%20%3D%20%5Cfrac%7B%20%5Cint_0%5E%7B%5Cpi/2%7D%20%28y%5E2-y%29%28sin%28x%29-y%29%5C%3Bdx%20%7D%7B%5Cint_0%5E%7B%5Cpi/2%7D%20%28y%5E2-y%29%5E2%5C%3Bdx%7D.gif

不确定这是否是P = 0.225。如果不是那么P的值可能是一个改进。 (除非选择P的其他值来保留其他一些无证件的财产)。

您可以通过添加额外的更正术语来提高准确性。给出类似return (1-a-b)*y + a y * abs(y) + b y * y * abs(y)的表格。我会以与上面相同的方式找到ab,这次给出了ab中两个线性方程组的系统来解决,而不是一个p中的等式。我不打算推​​导它,因为它的繁琐和乳胶图像的转换是痛苦的......;)

注意:在回答另一个问题时,我想到了P的另一个有效选择。 问题是使用反射将曲线延伸到(-pi,0)会在x = 0处的曲线中留下扭结。但是我怀疑我们可以选择P使得扭结变得平滑。 为此,在x = 0处取左右导数并确保它们相等。这给出了P的等式。

答案 5 :(得分:2)

怎么样?

x*(0.0174532925199433-8.650935142277599*10^-7*x^2)

表示deg和

x*(1-0.162716259904269*x^2)

对于rad分别是-45,45和-pi/4pi/4

答案 6 :(得分:1)

这(即fastsin函数)使用抛物线近似正弦函数。我怀疑它只适用于-π和+π之间的值。幸运的是,你可以继续增加或减去2π,直到你进入这个范围。 (编辑使用抛物线指定近似正弦函数的内容。)

答案 7 :(得分:1)

你可以使用这种近似值。

此解决方案使用二次曲线:

http://www.starming.com/index.php?action=plugin&v=wave&ajax=iframe&iframe=fullviewonepost&mid=56&tid=4825