由于sin() 是一个周期函数,我不应该超过一个周期来计算它。这简化了太多的数学运算,因为您永远不需要计算很大的阶乘数。实际上,您甚至不需要计算系列中每个项的阶乘,因为系数可以从最后一项导出,只需将前一个系数除以(n-1) 和n。如果您的输入仅限于一个周期(好吧,您不需要使用固定周期M_PI,您可以假设最大值为3.5,并通过仅减少除以M_PI 的模数。
一旦这样说,我们可以绑定你的最大误差,至于3.5 的最大输入,我们将3.5^n/n! 作为我们近似的最后一项,其中一些n 的边界小于一些修正我们需要计算的项数的最大误差。
我不会试图精确计算需要计算的项数,而是尝试做一些猜测,从推导算法和显示实际值(例如,3.2 的最大输入值)
这些是n 位置处术语的值,用于输入3.2 和
n | term at position n for input `3.2`
======+=================
8 | 0.27269634
12 | 0.00240693
16 | 0.00000578
18 | 0.00000019
20 | 0.00000001
21 | 7.9E-10
所以我们可以停止只计算系列的 20 项。 exp() 函数确实如此,它添加了所有术语并且是一个简单的函数。对于sin() 或cos(),如果您认为两者具有exp() 函数的相同项,您可以猜出更好的误差估计,(第一个只有奇数项,第二个只有偶数项)
(x^n)/(n!) - (x^(n+2))/((n+2)!) = (n!*x^n*(1 - x^2/((n+1)*(n+2))))/n!
n > 3.2 表示每个词是
< x^n/n!
所以我们可以应用与指数相同的标准。
这意味着我们可以在某个点停下来......如果我们继续我们的表格,我们会看到,例如n > 30,总的累积期限小于5.3E-18,所以我们可以停在那里(例如double 号码,至少)。
#include <stdio.h>
#include <math.h> /* for the system sin() function */
double MySin(double x) /* x must be in the range [0..3.2] */
{
int i;
const int n = 30;
double t = x, acum = x; /* first term, x/1! */
x *= x; /* square the argument so we get x^2 in variable x */
for (i = 3; i < n; i += 2) {
t = -t * x / i / (i-1); /* mutiply by -1, x^2 and divide by i and (i-1) */
acum += t; /* and add it to the accum */
}
return acum;
}
int main()
{
double arg;
for(;;) {
if (scanf("%lg", &arg) != 1)
break;
printf("MySin(%lg) = %lg; sin(%lg) = %lg\n",
arg, MySin(arg), arg, sin(arg));
}
}
如果你利用 sin 函数的对称性,你可以将你的域减少到小于 1 的M_PI/4,你甚至可以在 18 次方处停止以获得大约 17 个有效数字(对于double) 让你更快犯罪。
最后,我们可以得到一个对所有真实域都有效的sin2()函数:
double sin2(double x)
{
bool neg = false;
int ip = x / 2.0 / M_PI;
x -= 2.0 * M_PI * ip; /* reduce to first period [-2PI..2PI] */
if (x < 0.0) x += 2.0*M_PI; /* reduce to first period [0..2PI] */
if (x > M_PI) { x -= M_PI; neg = true; } /* ... first period negative [ 0..PI ] */
if (x > M_PI/2.0) x = M_PI - x; /* reflection [0..PI/2] */
return neg ? MySin(-x) : MySin(x);
}