diff options
Diffstat (limited to 'libm/double/polmisc.c')
| -rw-r--r-- | libm/double/polmisc.c | 309 | 
1 files changed, 309 insertions, 0 deletions
| diff --git a/libm/double/polmisc.c b/libm/double/polmisc.c new file mode 100644 index 000000000..7d517ae69 --- /dev/null +++ b/libm/double/polmisc.c @@ -0,0 +1,309 @@ + +/* Square root, sine, cosine, and arctangent of polynomial. + * See polyn.c for data structures and discussion. + */ + +#include <stdio.h> +#include <math.h> +#ifdef ANSIPROT +extern double atan2 ( double, double ); +extern double sqrt ( double ); +extern double fabs ( double ); +extern double sin ( double ); +extern double cos ( double ); +extern void polclr ( double *a, int n ); +extern void polmov ( double *a, int na, double *b ); +extern void polmul ( double a[], int na, double b[], int nb, double c[] ); +extern void poladd ( double a[], int na, double b[], int nb, double c[] ); +extern void polsub ( double a[], int na, double b[], int nb, double c[] ); +extern int poldiv ( double a[], int na, double b[], int nb, double c[] ); +extern void polsbt ( double a[], int na, double b[], int nb, double c[] ); +extern void * malloc ( long ); +extern void free ( void * ); +#else +double atan2(), sqrt(), fabs(), sin(), cos(); +void polclr(), polmov(), polsbt(), poladd(), polsub(), polmul(); +int poldiv(); +void * malloc(); +void free (); +#endif + +/* Highest degree of polynomial to be handled +   by the polyn.c subroutine package.  */ +#define N 16 +/* Highest degree actually initialized at runtime.  */ +extern int MAXPOL; + +/* Taylor series coefficients for various functions + */ +double patan[N+1] = { +  0.0,     1.0,      0.0, -1.0/3.0,     0.0, +  1.0/5.0, 0.0, -1.0/7.0,      0.0, 1.0/9.0, 0.0, -1.0/11.0, +  0.0, 1.0/13.0, 0.0, -1.0/15.0, 0.0 }; + +double psin[N+1] = { +  0.0, 1.0, 0.0,   -1.0/6.0,  0.0, 1.0/120.0,  0.0, +  -1.0/5040.0, 0.0, 1.0/362880.0, 0.0, -1.0/39916800.0, +  0.0, 1.0/6227020800.0, 0.0, -1.0/1.307674368e12, 0.0}; + +double pcos[N+1] = { +  1.0, 0.0,   -1.0/2.0,  0.0, 1.0/24.0,  0.0, +  -1.0/720.0, 0.0, 1.0/40320.0, 0.0, -1.0/3628800.0, 0.0, +  1.0/479001600.0, 0.0, -1.0/8.7179291e10, 0.0, 1.0/2.0922789888e13}; + +double pasin[N+1] = { +  0.0,     1.0,  0.0, 1.0/6.0,  0.0, +  3.0/40.0, 0.0, 15.0/336.0, 0.0, 105.0/3456.0, 0.0, 945.0/42240.0, +  0.0, 10395.0/599040.0 , 0.0, 135135.0/9676800.0 , 0.0 +}; + +/* Square root of 1 + x.  */ +double psqrt[N+1] = { +  1.0, 1./2., -1./8., 1./16., -5./128., 7./256., -21./1024., 33./2048., +  -429./32768., 715./65536., -2431./262144., 4199./524288., -29393./4194304., +  52003./8388608., -185725./33554432., 334305./67108864., +  -9694845./2147483648.}; + +/* Arctangent of the ratio num/den of two polynomials. + */ +void +polatn( num, den, ans, nn ) +     double num[], den[], ans[]; +     int nn; +{ +  double a, t; +  double *polq, *polu, *polt; +  int i; + +  if (nn > N) +    { +      mtherr ("polatn", OVERFLOW); +      return; +    } +  /* arctan( a + b ) = arctan(a) + arctan( b/(1 + ab + a**2) ) */ +  t = num[0]; +  a = den[0]; +  if( (t == 0.0) && (a == 0.0 ) ) +    { +      t = num[1]; +      a = den[1]; +    } +  t = atan2( t, a );  /* arctan(num/den), the ANSI argument order */ +  polq = (double * )malloc( (MAXPOL+1) * sizeof (double) ); +  polu = (double * )malloc( (MAXPOL+1) * sizeof (double) ); +  polt = (double * )malloc( (MAXPOL+1) * sizeof (double) ); +  polclr( polq, MAXPOL ); +  i = poldiv( den, nn, num, nn, polq ); +  a = polq[0]; /* a */ +  polq[0] = 0.0; /* b */ +  polmov( polq, nn, polu ); /* b */ +  /* Form the polynomial +     1 + ab + a**2 +     where a is a scalar.  */ +  for( i=0; i<=nn; i++ ) +    polu[i] *= a; +  polu[0] += 1.0 + a * a; +  poldiv( polu, nn, polq, nn, polt ); /* divide into b */ +  polsbt( polt, nn, patan, nn, polu ); /* arctan(b)  */ +  polu[0] += t; /* plus arctan(a) */ +  polmov( polu, nn, ans ); +  free( polt ); +  free( polu ); +  free( polq ); +} + + + +/* Square root of a polynomial. + * Assumes the lowest degree nonzero term is dominant + * and of even degree.  An error message is given + * if the Newton iteration does not converge. + */ +void +polsqt( pol, ans, nn ) +     double pol[], ans[]; +     int nn; +{ +  double t; +  double *x, *y; +  int i, n; +#if 0 +  double z[N+1]; +  double u; +#endif + +  if (nn > N) +    { +      mtherr ("polatn", OVERFLOW); +      return; +    } +  x = (double * )malloc( (MAXPOL+1) * sizeof (double) ); +  y = (double * )malloc( (MAXPOL+1) * sizeof (double) ); +  polmov( pol, nn, x ); +  polclr( y, MAXPOL ); + +  /* Find lowest degree nonzero term.  */ +  t = 0.0; +  for( n=0; n<nn; n++ ) +    { +      if( x[n] != 0.0 ) +	goto nzero; +    } +  polmov( y, nn, ans ); +  return; + +nzero: + +  if( n > 0 ) +    { +      if (n & 1) +        { +	  printf("error, sqrt of odd polynomial\n"); +	  return; +	} +      /* Divide by x^n.  */ +      y[n] = x[n]; +      poldiv (y, nn, pol, N, x); +    } + +  t = x[0]; +  for( i=1; i<=nn; i++ ) +    x[i] /= t; +  x[0] = 0.0; +  /* series development sqrt(1+x) = 1  +  x / 2  -  x**2 / 8  +  x**3 / 16 +     hopes that first (constant) term is greater than what follows   */ +  polsbt( x, nn, psqrt, nn, y); +  t = sqrt( t ); +  for( i=0; i<=nn; i++ ) +    y[i] *= t; + +  /* If first nonzero coefficient was at degree n > 0, multiply by +     x^(n/2).  */ +  if (n > 0) +    { +      polclr (x, MAXPOL); +      x[n/2] = 1.0; +      polmul (x, nn, y, nn, y); +    } +#if 0 +/* Newton iterations */ +for( n=0; n<10; n++ ) +	{ +	poldiv( y, nn, pol, nn, z ); +	poladd( y, nn, z, nn, y ); +	for( i=0; i<=nn; i++ ) +		y[i] *= 0.5; +	for( i=0; i<=nn; i++ ) +		{ +		u = fabs( y[i] - z[i] ); +		if( u > 1.0e-15 ) +			goto more; +		} +	goto done; +more:	; +	} +printf( "square root did not converge\n" ); +done: +#endif /* 0 */ + +polmov( y, nn, ans ); +free( y ); +free( x ); +} + + + +/* Sine of a polynomial. + * The computation uses + *     sin(a+b) = sin(a) cos(b) + cos(a) sin(b) + * where a is the constant term of the polynomial and + * b is the sum of the rest of the terms. + * Since sin(b) and cos(b) are computed by series expansions, + * the value of b should be small. + */ +void +polsin( x, y, nn ) +     double x[], y[]; +     int nn; +{ +  double a, sc; +  double *w, *c; +  int i; + +  if (nn > N) +    { +      mtherr ("polatn", OVERFLOW); +      return; +    } +  w = (double * )malloc( (MAXPOL+1) * sizeof (double) ); +  c = (double * )malloc( (MAXPOL+1) * sizeof (double) ); +  polmov( x, nn, w ); +  polclr( c, MAXPOL ); +  polclr( y, nn ); +  /* a, in the description, is x[0].  b is the polynomial x - x[0].  */ +  a = w[0]; +  /* c = cos (b) */ +  w[0] = 0.0; +  polsbt( w, nn, pcos, nn, c ); +  sc = sin(a); +  /* sin(a) cos (b) */ +  for( i=0; i<=nn; i++ ) +    c[i] *= sc; +  /* y = sin (b)  */ +  polsbt( w, nn, psin, nn, y ); +  sc = cos(a); +  /* cos(a) sin(b) */ +  for( i=0; i<=nn; i++ ) +    y[i] *= sc; +  poladd( c, nn, y, nn, y ); +  free( c ); +  free( w ); +} + + +/* Cosine of a polynomial. + * The computation uses + *     cos(a+b) = cos(a) cos(b) - sin(a) sin(b) + * where a is the constant term of the polynomial and + * b is the sum of the rest of the terms. + * Since sin(b) and cos(b) are computed by series expansions, + * the value of b should be small. + */ +void +polcos( x, y, nn ) +     double x[], y[]; +     int nn; +{ +  double a, sc; +  double *w, *c; +  int i; +  double sin(), cos(); + +  if (nn > N) +    { +      mtherr ("polatn", OVERFLOW); +      return; +    } +  w = (double * )malloc( (MAXPOL+1) * sizeof (double) ); +  c = (double * )malloc( (MAXPOL+1) * sizeof (double) ); +  polmov( x, nn, w ); +  polclr( c, MAXPOL ); +  polclr( y, nn ); +  a = w[0]; +  w[0] = 0.0; +  /* c = cos(b)  */ +  polsbt( w, nn, pcos, nn, c ); +  sc = cos(a); +  /* cos(a) cos(b)  */ +  for( i=0; i<=nn; i++ ) +    c[i] *= sc; +  /* y = sin(b) */ +  polsbt( w, nn, psin, nn, y ); +  sc = sin(a); +  /* sin(a) sin(b) */ +  for( i=0; i<=nn; i++ ) +    y[i] *= sc; +  polsub( y, nn, c, nn, y ); +  free( c ); +  free( w ); +} | 
