我一直在研究。net反汇编和GCC源代码,但似乎在任何地方都找不到sin()和其他数学函数的实际实现…他们似乎总是在引用别的东西。

有人能帮我找到他们吗?我觉得不太可能运行C语言的所有硬件都支持三角函数,所以一定有软件算法,对吧?


我知道有几种方法可以计算函数,并且为了好玩,我写了自己的例程来使用泰勒级数计算函数。我很好奇生产语言是如何做到这一点的,因为我的所有实现总是慢几个数量级,尽管我认为我的算法非常聪明(显然不是)。


当前回答

对于罪恶,用泰勒展开可以得到

Sin (x) = x - x^3/3!+ x ^ 5/5 !- x ^ 7/7 !+……(1)

您将继续添加项,直到它们之间的差异低于可接受的容忍水平,或者只是有限的步数(更快,但不太精确)。举个例子:

float sin(float x)
{
  float res=0, pow=x, fact=1;
  for(int i=0; i<5; ++i)
  {
    res+=pow/fact;
    pow*=-1*x*x;
    fact*=(2*(i+1))*(2*(i+1)+1);
  }

  return res;
}

注:(1)适用于小角度的近似值sin(x)=x。对于更大的角度,你需要计算越来越多的项才能得到可接受的结果。 你可以使用while参数并继续,以达到一定的准确性:

double sin (double x){
    int i = 1;
    double cur = x;
    double acc = 1;
    double fact= 1;
    double pow = x;
    while (fabs(acc) > .00000001 &&   i < 100){
        fact *= ((2*i)*(2*i+1));
        pow *= -1 * x*x; 
        acc =  pow / fact;
        cur += acc;
        i++;
    }
    return cur;

}

其他回答

像正弦和余弦这样的函数是在微处理器内部的微码中实现的。例如,英特尔芯片就有相应的组装指令。C编译器将生成调用这些汇编指令的代码。(相反,Java编译器不会。Java在软件而不是硬件中计算三角函数,因此运行速度要慢得多。)

芯片不使用泰勒级数来计算三角函数,至少不完全是这样。首先,他们使用CORDIC,但他们也可能使用一个短的泰勒级数来优化CORDIC的结果,或者用于特殊情况,例如在非常小的角度下以相对较高的精度计算正弦。有关更多解释,请参阅StackOverflow的回答。

正如许多人指出的那样,它依赖于实现。但就我对你的问题的理解而言,你对数学函数的真正软件实现感兴趣,但只是没有找到一个。如果是这样的话,那么你是这样的:

从http://ftp.gnu.org/gnu/glibc/下载glibc源代码 查看位于解包的glibc根\sysdeps\ieee754\dbl-64文件夹中的文件dosincosc 类似地,您可以找到其余数学库的实现,只需查找具有适当名称的文件

您也可以看看扩展名为.tbl的文件,它们的内容只不过是以二进制形式的不同函数的预计算值的巨大表格。这就是为什么实现如此之快:而不是计算他们使用的任何级数的所有系数,他们只是做一个快速查找,这要快得多。顺便说一下,他们确实用裁缝级数来计算正弦和余弦。

我希望这能有所帮助。

它如何做到这一点的本质在于杰拉德·惠特利的《应用数值分析》节选:

当你的软件程序要求计算机获取一个值时 或者,你有没有想过它是如何得到 如果它能计算的最强大的函数是多项式? 它不会在表中查找这些并进行插值!相反, 计算机逼近除多项式以外的所有函数 一个多项式,可以精确地给出值。

上面要提到的几点是,一些算法实际上是从表中插值的,尽管只是在前几次迭代中。还要注意它是如何提到计算机利用近似多项式而没有指定哪种类型的近似多项式。正如本文中其他人指出的那样,在这种情况下,切比雪夫多项式比泰勒多项式更有效。

计算正弦/余弦/正切其实很容易通过代码使用泰勒级数来实现。自己写一个只需5秒钟。

整个过程可以用这个方程来概括:

下面是我为C语言写的一些例程:

double _pow(double a, double b) {
    double c = 1;
    for (int i=0; i<b; i++)
        c *= a;
    return c;
}

double _fact(double x) {
    double ret = 1;
    for (int i=1; i<=x; i++) 
        ret *= i;
    return ret;
}

double _sin(double x) {
    double y = x;
    double s = -1;
    for (int i=3; i<=100; i+=2) {
        y+=s*(_pow(x,i)/_fact(i));
        s *= -1;
    }  
    return y;
}
double _cos(double x) {
    double y = 1;
    double s = -1;
    for (int i=2; i<=100; i+=2) {
        y+=s*(_pow(x,i)/_fact(i));
        s *= -1;
    }  
    return y;
}
double _tan(double x) {
     return (_sin(x)/_cos(x));  
}

它们通常在软件中实现,在大多数情况下不会使用相应的硬件(即汇编)调用。然而,正如Jason所指出的,这些是特定于实现的。

请注意,这些软件例程不是编译器源代码的一部分,而是可以在相应的库中找到,例如clib或GNU编译器的glibc。看到http://www.gnu.org/software/libc/manual/html_mono/libc.html三角函数

如果你想要更大的控制权,你应该仔细评估你到底需要什么。一些典型的方法是查找表的插值、程序集调用(通常很慢)或其他近似方案,如Newton-Raphson的平方根。