我一直在研究。net反汇编和GCC源代码,但似乎在任何地方都找不到sin()和其他数学函数的实际实现…他们似乎总是在引用别的东西。
有人能帮我找到他们吗?我觉得不太可能运行C语言的所有硬件都支持三角函数,所以一定有软件算法,对吧?
我知道有几种方法可以计算函数,并且为了好玩,我写了自己的例程来使用泰勒级数计算函数。我很好奇生产语言是如何做到这一点的,因为我的所有实现总是慢几个数量级,尽管我认为我的算法非常聪明(显然不是)。
我一直在研究。net反汇编和GCC源代码,但似乎在任何地方都找不到sin()和其他数学函数的实际实现…他们似乎总是在引用别的东西。
有人能帮我找到他们吗?我觉得不太可能运行C语言的所有硬件都支持三角函数,所以一定有软件算法,对吧?
我知道有几种方法可以计算函数,并且为了好玩,我写了自己的例程来使用泰勒级数计算函数。我很好奇生产语言是如何做到这一点的,因为我的所有实现总是慢几个数量级,尽管我认为我的算法非常聪明(显然不是)。
当前回答
计算正弦/余弦/正切其实很容易通过代码使用泰勒级数来实现。自己写一个只需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));
}
其他回答
计算正弦/余弦/正切其实很容易通过代码使用泰勒级数来实现。自己写一个只需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));
}
如果你想要一个软件实现,而不是硬件实现,可以在《数值公式》的第5章中找到这个问题的明确答案。我的副本在一个盒子里,所以我不能给出细节,但简短的版本(如果我没记错的话)是你把tan(theta/2)作为你的基本操作,然后从那里计算其他的。计算是用级数近似完成的,但它比泰勒级数收敛得快得多。
抱歉,我没拿到书就想不起来了。
库函数的实际实现取决于特定的编译器和/或库提供程序。不管它是用硬件还是软件,不管它是不是泰勒展开,等等,都会有所不同。
我意识到这完全没有帮助。
关于sin(), cos(),tan()这样的三角函数,在5年之后,没有提到高质量三角函数的一个重要方面:极差约简。
任何这些函数的早期步骤都是将角度(以弧度为单位)减小到2*π区间。但是π是无理数,所以像x =余数(x, 2*M_PI)这样的简单简化会引入误差,因为M_PI或机器pi是π的近似值。那么,如何求x =余数(x, 2*π)呢?
早期的库使用扩展精度或精心设计的编程来提供高质量的结果,但仍然在有限的double范围内。当请求一个较大的值,如sin(pow(2,30))时,结果是无意义的或0.0,并且可能将错误标志设置为TLOSS完全损失精度或PLOSS部分损失精度。
将大的值缩小到像-π到π这样的区间是一个具有挑战性的问题,它可以与基本三角函数(比如sin())本身的挑战相媲美。
一个好的报告是大论点的论据缩减:好到最后一位(1992)。它涵盖了这个问题很好:讨论了需要和事情是如何在各种平台(SPARC, PC, HP, 30+其他),并提供了一个解决方案算法,为所有双从-DBL_MAX到DBL_MAX的高质量结果。
如果原始参数以度为单位,但可能值很大,则首先使用fmod()以提高精度。一个好的fmod()将不会引入任何错误,从而提供出色的范围缩小。
// sin(degrees2radians(x))
sin(degrees2radians(fmod(x, 360.0))); // -360.0 < fmod(x,360) < +360.0
各种三角恒等式和remquo()提供了更多的改进。示例:信德()
无论何时这样一个函数被求值,那么在某种程度上很可能有:
内插的值表(用于快速,不准确的应用程序-例如计算机图形) 收敛于期望值的级数的计算——可能不是泰勒级数,更可能是基于像克伦肖-柯蒂斯这样的奇异正交。
如果没有硬件支持,那么编译器可能会使用后一种方法,只发出汇编代码(没有调试符号),而不是使用c库——这让您在调试器中跟踪实际代码变得很棘手。