我编写了一个使用maclaurin系列计算sin的代码,但是当我尝试为大x值计算它并试图通过给出一个大的N阶(总和的长度)来抵消它时 - 最终它会溢出并没有给我正确的结果。这是代码,我想知道是否有一种额外的方法来优化它,因此它适用于大的x值(它已经适用于小x值和非常大的N值)。 这是代码:
long double calcMaclaurinPolynom(double x, int N){
long double result = 0;
long double atzeretCounter = 2;
int sign = 1;
long double fraction = x;
for (int i = 0; i <= N; i++)
{
result += sign*fraction;
sign = sign*(-1);
fraction = fraction*((x*x) / ((atzeretCounter)*(atzeretCounter + 1)));
atzeretCounter += 2;
}
return result;
}
答案 0 :(得分:2)
主要问题是使用超出其收敛范围的系列。
正如OP所说“转换x到radX =(x * PI)/ 180”表示OP以度而不是弧度开始,OP很幸运。找到my_sin(x)
的第一步是减少范围。以度数开始时,减少是准确的。因此,在转换为弧度之前,请缩小范围。
long double calcMaclaurinPolynom(double x /* degrees */, int N){
// Reduce to range -360 to 360
// This reduction is exact, no round-off error
x = fmod(x, 360);
// Reduce to range -180 to 180
if (x >= 180) {
x -= 180;
x = -x;
} else if (x <= -180) {
x += 180;
x = -x;
}
// Reduce to range -90 to 90
if (x >= 90) {
x = 180 - x;
} else if (x <= -90) {
x = -180 - x;
}
//now convert to radians.
x = x*PI/180;
// continue with regular code
替代方案,如果使用C11,请使用remquo()
。搜索SO以获取示例代码。
如上所述@ user3386109,无需“转换回度”。
[编辑]
对于典型的求和系列,将最不重要的术语首先相加可以提高答案的精确度。使用OP的代码,可以使用
完成for (int i = N; i >= 0; i--)
或者,不是迭代固定次数,而是循环直到该项对总和没有意义。以下使用递归来首先对最不重要的术语求和。范围缩小在-90到90范围内,迭代次数不会过多。
static double sin_d_helper(double term, double xx, unsigned i) {
if (1.0 + term == 1.0)
return term;
return term - sin_d_helper(term * xx / ((i + 1) * (i + 2)), xx, i + 2);
}
#include <math.h>
double sin_d(double x_degrees) {
// range reduction and d --> r conversion from above
double x_radians = ...
return x_radians * sin_d_helper(1.0, x_radians * x_radians, 1);
}
答案 1 :(得分:1)
您可以通过将符号变量合并到fraction
中的(-x*x)
更新中来避免使用该变量。
使用算法,您可以在阶乘中遇到整数溢出问题。
只要x*x < (2*k)*(2*k+1)
错误(假设完全评估)受abs(fraction)
限制,即系列中下一个字词的大小。
对于大x
,最大的错误来源是截断。浮点误差通过取消交替序列的项而放大。对于k
约x/2
,第k个术语的大小最大,必须被其他大项所抵消。
在不使用pi值的情况下处理大x
的一种简单方法是使用三角定理
sin(2*x)=2*sin(x)*cos(x)
cos(2*x)=2*cos(x)^2-1=cos(x)^2-sin(x)^2
首先通过减半来减少x
,同时评估sin(x/2^n)
和cos(x/2^n)
的Maclaurin系列,然后使用三角平方(文字平方作为复数cos(x)+i*sin(x)
)来恢复原始参数的值。
cos(x/2^(n-1)) = cos(x/2^n)^2-sin(x/2^n)^2
sin(x/2^(n-1)) = 2*sin(x/2^n)*cos(x/2^n)
然后
cos(x/2^(n-2)) = cos(x/2^(n-1))^2-sin(x/2^(n-1))^2
sin(x/2^(n-2)) = 2*sin(x/2^(n-1))*cos(x/2^(n-1))
等
有关sin和cos值的同时计算,请参见https://stackoverflow.com/a/22791396/3088138,然后用
封装它def CosSinForLargerX(x,n):
k=0
while abs(x)>1:
k+=1; x/=2
c,s = getCosSin(x,n)
r2=0
for i in range(k):
s2=s*s; c2=c*c; r2=s2+c2
s = 2*c*s
c = c2-s2
return c/r2,s/r2