diff options
author | Eric Andersen <andersen@codepoet.org> | 2001-05-10 00:40:28 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2001-05-10 00:40:28 +0000 |
commit | 1077fa4d772832f77a677ce7fb7c2d513b959e3f (patch) | |
tree | 579bee13fb0b58d2800206366ec2caecbb15f3fc /libm/float/jvf.c | |
parent | 22358dd7ce7bb49792204b698f01a6f69b9c8e08 (diff) |
uClibc now has a math library. muahahahaha!
-Erik
Diffstat (limited to 'libm/float/jvf.c')
-rw-r--r-- | libm/float/jvf.c | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/libm/float/jvf.c b/libm/float/jvf.c new file mode 100644 index 000000000..268a8e4eb --- /dev/null +++ b/libm/float/jvf.c @@ -0,0 +1,848 @@ +/* jvf.c + * + * Bessel function of noninteger order + * + * + * + * SYNOPSIS: + * + * float v, x, y, jvf(); + * + * y = jvf( v, x ); + * + * + * + * DESCRIPTION: + * + * Returns Bessel function of order v of the argument, + * where v is real. Negative x is allowed if v is an integer. + * + * Several expansions are included: the ascending power + * series, the Hankel expansion, and two transitional + * expansions for large v. If v is not too large, it + * is reduced by recurrence to a region of best accuracy. + * + * The single precision routine accepts negative v, but with + * reduced accuracy. + * + * + * + * ACCURACY: + * Results for integer v are indicated by *. + * Error criterion is absolute, except relative when |jv()| > 1. + * + * arithmetic domain # trials peak rms + * v x + * IEEE 0,125 0,125 30000 2.0e-6 2.0e-7 + * IEEE -17,0 0,125 30000 1.1e-5 4.0e-7 + * IEEE -100,0 0,125 3000 1.5e-4 7.8e-6 + */ + + +/* +Cephes Math Library Release 2.2: June, 1992 +Copyright 1984, 1987, 1989, 1992 by Stephen L. Moshier +Direct inquiries to 30 Frost Street, Cambridge, MA 02140 +*/ + + +#include <math.h> +#define DEBUG 0 + +extern float MAXNUMF, MACHEPF, MINLOGF, MAXLOGF, PIF; +extern int sgngamf; + +/* BIG = 1/MACHEPF */ +#define BIG 16777216. + +#ifdef ANSIC +float floorf(float), j0f(float), j1f(float); +static float jnxf(float, float); +static float jvsf(float, float); +static float hankelf(float, float); +static float jntf(float, float); +static float recurf( float *, float, float * ); +float sqrtf(float), sinf(float), cosf(float); +float lgamf(float), expf(float), logf(float), powf(float, float); +float gammaf(float), cbrtf(float), acosf(float); +int airyf(float, float *, float *, float *, float *); +float polevlf(float, float *, int); +#else +float floorf(), j0f(), j1f(); +float sqrtf(), sinf(), cosf(); +float lgamf(), expf(), logf(), powf(), gammaf(); +float cbrtf(), polevlf(), acosf(); +void airyf(); +static float recurf(), jvsf(), hankelf(), jnxf(), jntf(), jvsf(); +#endif + + +#define fabsf(x) ( (x) < 0 ? -(x) : (x) ) + +float jvf( float nn, float xx ) +{ +float n, x, k, q, t, y, an, sign; +int i, nint; + +n = nn; +x = xx; +nint = 0; /* Flag for integer n */ +sign = 1.0; /* Flag for sign inversion */ +an = fabsf( n ); +y = floorf( an ); +if( y == an ) + { + nint = 1; + i = an - 16384.0 * floorf( an/16384.0 ); + if( n < 0.0 ) + { + if( i & 1 ) + sign = -sign; + n = an; + } + if( x < 0.0 ) + { + if( i & 1 ) + sign = -sign; + x = -x; + } + if( n == 0.0 ) + return( j0f(x) ); + if( n == 1.0 ) + return( sign * j1f(x) ); + } + +if( (x < 0.0) && (y != an) ) + { + mtherr( "jvf", DOMAIN ); + y = 0.0; + goto done; + } + +y = fabsf(x); + +if( y < MACHEPF ) + goto underf; + +/* Easy cases - x small compared to n */ +t = 3.6 * sqrtf(an); +if( y < t ) + return( sign * jvsf(n,x) ); + +/* x large compared to n */ +k = 3.6 * sqrtf(y); +if( (an < k) && (y > 6.0) ) + return( sign * hankelf(n,x) ); + +if( (n > -100) && (n < 14.0) ) + { +/* Note: if x is too large, the continued + * fraction will fail; but then the + * Hankel expansion can be used. + */ + if( nint != 0 ) + { + k = 0.0; + q = recurf( &n, x, &k ); + if( k == 0.0 ) + { + y = j0f(x)/q; + goto done; + } + if( k == 1.0 ) + { + y = j1f(x)/q; + goto done; + } + } + + if( n >= 0.0 ) + { +/* Recur backwards from a larger value of n + */ + if( y > 1.3 * an ) + goto recurdwn; + if( an > 1.3 * y ) + goto recurdwn; + k = n; + y = 2.0*(y+an+1.0); + if( (y - n) > 33.0 ) + y = n + 33.0; + y = n + floorf(y-n); + q = recurf( &y, x, &k ); + y = jvsf(y,x) * q; + goto done; + } +recurdwn: + if( an > (k + 3.0) ) + { +/* Recur backwards from n to k + */ + if( n < 0.0 ) + k = -k; + q = n - floorf(n); + k = floorf(k) + q; + if( n > 0.0 ) + q = recurf( &n, x, &k ); + else + { + t = k; + k = n; + q = recurf( &t, x, &k ); + k = t; + } + if( q == 0.0 ) + { +underf: + y = 0.0; + goto done; + } + } + else + { + k = n; + q = 1.0; + } + +/* boundary between convergence of + * power series and Hankel expansion + */ + t = fabsf(k); + if( t < 26.0 ) + t = (0.0083*t + 0.09)*t + 12.9; + else + t = 0.9 * t; + + if( y > t ) /* y = |x| */ + y = hankelf(k,x); + else + y = jvsf(k,x); +#if DEBUG +printf( "y = %.16e, q = %.16e\n", y, q ); +#endif + if( n > 0.0 ) + y /= q; + else + y *= q; + } + +else + { +/* For large positive n, use the uniform expansion + * or the transitional expansion. + * But if x is of the order of n**2, + * these may blow up, whereas the + * Hankel expansion will then work. + */ + if( n < 0.0 ) + { + mtherr( "jvf", TLOSS ); + y = 0.0; + goto done; + } + t = y/an; + t /= an; + if( t > 0.3 ) + y = hankelf(n,x); + else + y = jnxf(n,x); + } + +done: return( sign * y); +} + +/* Reduce the order by backward recurrence. + * AMS55 #9.1.27 and 9.1.73. + */ + +static float recurf( float *n, float xx, float *newn ) +{ +float x, pkm2, pkm1, pk, pkp1, qkm2, qkm1; +float k, ans, qk, xk, yk, r, t, kf, xinv; +static float big = BIG; +int nflag, ctr; + +x = xx; +/* continued fraction for Jn(x)/Jn-1(x) */ +if( *n < 0.0 ) + nflag = 1; +else + nflag = 0; + +fstart: + +#if DEBUG +printf( "n = %.6e, newn = %.6e, cfrac = ", *n, *newn ); +#endif + +pkm2 = 0.0; +qkm2 = 1.0; +pkm1 = x; +qkm1 = *n + *n; +xk = -x * x; +yk = qkm1; +ans = 1.0; +ctr = 0; +do + { + yk += 2.0; + pk = pkm1 * yk + pkm2 * xk; + qk = qkm1 * yk + qkm2 * xk; + pkm2 = pkm1; + pkm1 = pk; + qkm2 = qkm1; + qkm1 = qk; + if( qk != 0 ) + r = pk/qk; + else + r = 0.0; + if( r != 0 ) + { + t = fabsf( (ans - r)/r ); + ans = r; + } + else + t = 1.0; + + if( t < MACHEPF ) + goto done; + + if( fabsf(pk) > big ) + { + pkm2 *= MACHEPF; + pkm1 *= MACHEPF; + qkm2 *= MACHEPF; + qkm1 *= MACHEPF; + } + } +while( t > MACHEPF ); + +done: + +#if DEBUG +printf( "%.6e\n", ans ); +#endif + +/* Change n to n-1 if n < 0 and the continued fraction is small + */ +if( nflag > 0 ) + { + if( fabsf(ans) < 0.125 ) + { + nflag = -1; + *n = *n - 1.0; + goto fstart; + } + } + + +kf = *newn; + +/* backward recurrence + * 2k + * J (x) = --- J (x) - J (x) + * k-1 x k k+1 + */ + +pk = 1.0; +pkm1 = 1.0/ans; +k = *n - 1.0; +r = 2 * k; +xinv = 1.0/x; +do + { + pkm2 = (pkm1 * r - pk * x) * xinv; + pkp1 = pk; + pk = pkm1; + pkm1 = pkm2; + r -= 2.0; +#if 0 + t = fabsf(pkp1) + fabsf(pk); + if( (k > (kf + 2.5)) && (fabsf(pkm1) < 0.25*t) ) + { + k -= 1.0; + t = x*x; + pkm2 = ( (r*(r+2.0)-t)*pk - r*x*pkp1 )/t; + pkp1 = pk; + pk = pkm1; + pkm1 = pkm2; + r -= 2.0; + } +#endif + k -= 1.0; + } +while( k > (kf + 0.5) ); + +#if 0 +/* Take the larger of the last two iterates + * on the theory that it may have less cancellation error. + */ +if( (kf >= 0.0) && (fabsf(pk) > fabsf(pkm1)) ) + { + k += 1.0; + pkm2 = pk; + } +#endif + +*newn = k; +#if DEBUG +printf( "newn %.6e\n", k ); +#endif +return( pkm2 ); +} + + + +/* Ascending power series for Jv(x). + * AMS55 #9.1.10. + */ + +static float jvsf( float nn, float xx ) +{ +float n, x, t, u, y, z, k, ay; + +#if DEBUG +printf( "jvsf: " ); +#endif +n = nn; +x = xx; +z = -0.25 * x * x; +u = 1.0; +y = u; +k = 1.0; +t = 1.0; + +while( t > MACHEPF ) + { + u *= z / (k * (n+k)); + y += u; + k += 1.0; + t = fabsf(u); + if( (ay = fabsf(y)) > 1.0 ) + t /= ay; + } + +if( x < 0.0 ) + { + y = y * powf( 0.5 * x, n ) / gammaf( n + 1.0 ); + } +else + { + t = n * logf(0.5*x) - lgamf(n + 1.0); + if( t < -MAXLOGF ) + { + return( 0.0 ); + } + if( t > MAXLOGF ) + { + t = logf(y) + t; + if( t > MAXLOGF ) + { + mtherr( "jvf", OVERFLOW ); + return( MAXNUMF ); + } + else + { + y = sgngamf * expf(t); + return(y); + } + } + y = sgngamf * y * expf( t ); + } +#if DEBUG +printf( "y = %.8e\n", y ); +#endif +return(y); +} + +/* Hankel's asymptotic expansion + * for large x. + * AMS55 #9.2.5. + */ +static float hankelf( float nn, float xx ) +{ +float n, x, t, u, z, k, sign, conv; +float p, q, j, m, pp, qq; +int flag; + +#if DEBUG +printf( "hankelf: " ); +#endif +n = nn; +x = xx; +m = 4.0*n*n; +j = 1.0; +z = 8.0 * x; +k = 1.0; +p = 1.0; +u = (m - 1.0)/z; +q = u; +sign = 1.0; +conv = 1.0; +flag = 0; +t = 1.0; +pp = 1.0e38; +qq = 1.0e38; + +while( t > MACHEPF ) + { + k += 2.0; + j += 1.0; + sign = -sign; + u *= (m - k * k)/(j * z); + p += sign * u; + k += 2.0; + j += 1.0; + u *= (m - k * k)/(j * z); + q += sign * u; + t = fabsf(u/p); + if( t < conv ) + { + conv = t; + qq = q; + pp = p; + flag = 1; + } +/* stop if the terms start getting larger */ + if( (flag != 0) && (t > conv) ) + { +#if DEBUG + printf( "Hankel: convergence to %.4E\n", conv ); +#endif + goto hank1; + } + } + +hank1: +u = x - (0.5*n + 0.25) * PIF; +t = sqrtf( 2.0/(PIF*x) ) * ( pp * cosf(u) - qq * sinf(u) ); +return( t ); +} + + +/* Asymptotic expansion for large n. + * AMS55 #9.3.35. + */ + +static float lambda[] = { + 1.0, + 1.041666666666666666666667E-1, + 8.355034722222222222222222E-2, + 1.282265745563271604938272E-1, + 2.918490264641404642489712E-1, + 8.816272674437576524187671E-1, + 3.321408281862767544702647E+0, + 1.499576298686255465867237E+1, + 7.892301301158651813848139E+1, + 4.744515388682643231611949E+2, + 3.207490090890661934704328E+3 +}; +static float mu[] = { + 1.0, + -1.458333333333333333333333E-1, + -9.874131944444444444444444E-2, + -1.433120539158950617283951E-1, + -3.172272026784135480967078E-1, + -9.424291479571202491373028E-1, + -3.511203040826354261542798E+0, + -1.572726362036804512982712E+1, + -8.228143909718594444224656E+1, + -4.923553705236705240352022E+2, + -3.316218568547972508762102E+3 +}; +static float P1[] = { + -2.083333333333333333333333E-1, + 1.250000000000000000000000E-1 +}; +static float P2[] = { + 3.342013888888888888888889E-1, + -4.010416666666666666666667E-1, + 7.031250000000000000000000E-2 +}; +static float P3[] = { + -1.025812596450617283950617E+0, + 1.846462673611111111111111E+0, + -8.912109375000000000000000E-1, + 7.324218750000000000000000E-2 +}; +static float P4[] = { + 4.669584423426247427983539E+0, + -1.120700261622299382716049E+1, + 8.789123535156250000000000E+0, + -2.364086914062500000000000E+0, + 1.121520996093750000000000E-1 +}; +static float P5[] = { + -2.8212072558200244877E1, + 8.4636217674600734632E1, + -9.1818241543240017361E1, + 4.2534998745388454861E1, + -7.3687943594796316964E0, + 2.27108001708984375E-1 +}; +static float P6[] = { + 2.1257013003921712286E2, + -7.6525246814118164230E2, + 1.0599904525279998779E3, + -6.9957962737613254123E2, + 2.1819051174421159048E2, + -2.6491430486951555525E1, + 5.7250142097473144531E-1 +}; +static float P7[] = { + -1.9194576623184069963E3, + 8.0617221817373093845E3, + -1.3586550006434137439E4, + 1.1655393336864533248E4, + -5.3056469786134031084E3, + 1.2009029132163524628E3, + -1.0809091978839465550E2, + 1.7277275025844573975E0 +}; + + +static float jnxf( float nn, float xx ) +{ +float n, x, zeta, sqz, zz, zp, np; +float cbn, n23, t, z, sz; +float pp, qq, z32i, zzi; +float ak, bk, akl, bkl; +int sign, doa, dob, nflg, k, s, tk, tkp1, m; +static float u[8]; +static float ai, aip, bi, bip; + +n = nn; +x = xx; +/* Test for x very close to n. + * Use expansion for transition region if so. + */ +cbn = cbrtf(n); +z = (x - n)/cbn; +if( (fabsf(z) <= 0.7) || (n < 0.0) ) + return( jntf(n,x) ); +z = x/n; +zz = 1.0 - z*z; +if( zz == 0.0 ) + return(0.0); + +if( zz > 0.0 ) + { + sz = sqrtf( zz ); + t = 1.5 * (logf( (1.0+sz)/z ) - sz ); /* zeta ** 3/2 */ + zeta = cbrtf( t * t ); + nflg = 1; + } +else + { + sz = sqrtf(-zz); + t = 1.5 * (sz - acosf(1.0/z)); + zeta = -cbrtf( t * t ); + nflg = -1; + } +z32i = fabsf(1.0/t); +sqz = cbrtf(t); + +/* Airy function */ +n23 = cbrtf( n * n ); +t = n23 * zeta; + +#if DEBUG +printf("zeta %.5E, Airyf(%.5E)\n", zeta, t ); +#endif +airyf( t, &ai, &aip, &bi, &bip ); + +/* polynomials in expansion */ +u[0] = 1.0; +zzi = 1.0/zz; +u[1] = polevlf( zzi, P1, 1 )/sz; +u[2] = polevlf( zzi, P2, 2 )/zz; +u[3] = polevlf( zzi, P3, 3 )/(sz*zz); +pp = zz*zz; +u[4] = polevlf( zzi, P4, 4 )/pp; +u[5] = polevlf( zzi, P5, 5 )/(pp*sz); +pp *= zz; +u[6] = polevlf( zzi, P6, 6 )/pp; +u[7] = polevlf( zzi, P7, 7 )/(pp*sz); + +#if DEBUG +for( k=0; k<=7; k++ ) + printf( "u[%d] = %.5E\n", k, u[k] ); +#endif + +pp = 0.0; +qq = 0.0; +np = 1.0; +/* flags to stop when terms get larger */ +doa = 1; +dob = 1; +akl = MAXNUMF; +bkl = MAXNUMF; + +for( k=0; k<=3; k++ ) + { + tk = 2 * k; + tkp1 = tk + 1; + zp = 1.0; + ak = 0.0; + bk = 0.0; + for( s=0; s<=tk; s++ ) + { + if( doa ) + { + if( (s & 3) > 1 ) + sign = nflg; + else + sign = 1; + ak += sign * mu[s] * zp * u[tk-s]; + } + + if( dob ) + { + m = tkp1 - s; + if( ((m+1) & 3) > 1 ) + sign = nflg; + else + sign = 1; + bk += sign * lambda[s] * zp * u[m]; + } + zp *= z32i; + } + + if( doa ) + { + ak *= np; + t = fabsf(ak); + if( t < akl ) + { + akl = t; + pp += ak; + } + else + doa = 0; + } + + if( dob ) + { + bk += lambda[tkp1] * zp * u[0]; + bk *= -np/sqz; + t = fabsf(bk); + if( t < bkl ) + { + bkl = t; + qq += bk; + } + else + dob = 0; + } +#if DEBUG + printf("a[%d] %.5E, b[%d] %.5E\n", k, ak, k, bk ); +#endif + if( np < MACHEPF ) + break; + np /= n*n; + } + +/* normalizing factor ( 4*zeta/(1 - z**2) )**1/4 */ +t = 4.0 * zeta/zz; +t = sqrtf( sqrtf(t) ); + +t *= ai*pp/cbrtf(n) + aip*qq/(n23*n); +return(t); +} + +/* Asymptotic expansion for transition region, + * n large and x close to n. + * AMS55 #9.3.23. + */ + +static float PF2[] = { + -9.0000000000000000000e-2, + 8.5714285714285714286e-2 +}; +static float PF3[] = { + 1.3671428571428571429e-1, + -5.4920634920634920635e-2, + -4.4444444444444444444e-3 +}; +static float PF4[] = { + 1.3500000000000000000e-3, + -1.6036054421768707483e-1, + 4.2590187590187590188e-2, + 2.7330447330447330447e-3 +}; +static float PG1[] = { + -2.4285714285714285714e-1, + 1.4285714285714285714e-2 +}; +static float PG2[] = { + -9.0000000000000000000e-3, + 1.9396825396825396825e-1, + -1.1746031746031746032e-2 +}; +static float PG3[] = { + 1.9607142857142857143e-2, + -1.5983694083694083694e-1, + 6.3838383838383838384e-3 +}; + + +static float jntf( float nn, float xx ) +{ +float n, x, z, zz, z3; +float cbn, n23, cbtwo; +float ai, aip, bi, bip; /* Airy functions */ +float nk, fk, gk, pp, qq; +float F[5], G[4]; +int k; + +n = nn; +x = xx; +cbn = cbrtf(n); +z = (x - n)/cbn; +cbtwo = cbrtf( 2.0 ); + +/* Airy function */ +zz = -cbtwo * z; +airyf( zz, &ai, &aip, &bi, &bip ); + +/* polynomials in expansion */ +zz = z * z; +z3 = zz * z; +F[0] = 1.0; +F[1] = -z/5.0; +F[2] = polevlf( z3, PF2, 1 ) * zz; +F[3] = polevlf( z3, PF3, 2 ); +F[4] = polevlf( z3, PF4, 3 ) * z; +G[0] = 0.3 * zz; +G[1] = polevlf( z3, PG1, 1 ); +G[2] = polevlf( z3, PG2, 2 ) * z; +G[3] = polevlf( z3, PG3, 2 ) * zz; +#if DEBUG +for( k=0; k<=4; k++ ) + printf( "F[%d] = %.5E\n", k, F[k] ); +for( k=0; k<=3; k++ ) + printf( "G[%d] = %.5E\n", k, G[k] ); +#endif +pp = 0.0; +qq = 0.0; +nk = 1.0; +n23 = cbrtf( n * n ); + +for( k=0; k<=4; k++ ) + { + fk = F[k]*nk; + pp += fk; + if( k != 4 ) + { + gk = G[k]*nk; + qq += gk; + } +#if DEBUG + printf("fk[%d] %.5E, gk[%d] %.5E\n", k, fk, k, gk ); +#endif + nk /= n23; + } + +fk = cbtwo * ai * pp/cbn + cbrtf(4.0) * aip * qq/n; +return(fk); +} |