double npy_log2(double x) { #ifdef HAVE_FREXP if (!npy_isfinite(x) || x <= 0.) { /* special value result */ return npy_log(x); } else { /* * fallback implementation copied from python3.4 math.log2 * provides int(log(2**i)) == i for i 1-64 in default rounding mode. * * We want log2(m * 2**e) == log(m) / log(2) + e. Care is needed when * x is just greater than 1.0: in that case e is 1, log(m) is negative, * and we get significant cancellation error from the addition of * log(m) / log(2) to e. The slight rewrite of the expression below * avoids this problem. */ int e; double m = frexp(x, &e); if (x >= 1.0) { return log(2.0 * m) / log(2.0) + (e - 1); } else { return log(m) / log(2.0) + e; } } #else /* does not provide int(log(2**i)) == i */ return NPY_LOG2E * npy_log(x); #endif }
/* * Bessel series * http://dlmf.nist.gov/11.4.19 */ double struve_bessel_series(double v, double z, int is_h, double *err) { int n, sgn; double term, cterm, sum, maxterm; if (is_h && v < 0) { /* Works less reliably in this region */ *err = NPY_INFINITY; return NPY_NAN; } sum = 0; maxterm = 0; cterm = sqrt(z / (2*M_PI)); for (n = 0; n < MAXITER; ++n) { if (is_h) { term = cterm * bessel_j(n + v + 0.5, z) / (n + 0.5); cterm *= z/2 / (n + 1); } else { term = cterm * bessel_i(n + v + 0.5, z) / (n + 0.5); cterm *= -z/2 / (n + 1); } sum += term; if (fabs(term) > maxterm) { maxterm = fabs(term); } if (fabs(term) < SUM_EPS * fabs(sum) || term == 0 || !npy_isfinite(sum)) { break; } } *err = fabs(term) + fabs(maxterm) * 1e-16; /* Account for potential underflow of the Bessel functions */ *err += 1e-300 * fabs(cterm); return sum; }
/* * Large-z expansion for Struve H and L * http://dlmf.nist.gov/11.6.1 */ double struve_asymp_large_z(double v, double z, int is_h, double *err) { int n, sgn, maxiter; double term, sum, maxterm; double m; if (is_h) { sgn = -1; } else { sgn = 1; } /* Asymptotic expansion divergenge point */ m = z/2; if (m <= 0) { maxiter = 0; } else if (m > MAXITER) { maxiter = MAXITER; } else { maxiter = (int)m; } if (maxiter == 0) { *err = NPY_INFINITY; return NPY_NAN; } if (z < v) { /* Exclude regions where our error estimation fails */ *err = NPY_INFINITY; return NPY_NAN; } /* Evaluate sum */ term = -sgn / sqrt(M_PI) * exp(-lgam(v + 0.5) + (v - 1) * log(z/2)) * gammasgn(v + 0.5); sum = term; maxterm = 0; for (n = 0; n < maxiter; ++n) { term *= sgn * (1 + 2*n) * (1 + 2*n - 2*v) / (z*z); sum += term; if (fabs(term) > maxterm) { maxterm = fabs(term); } if (fabs(term) < SUM_EPS * fabs(sum) || term == 0 || !npy_isfinite(sum)) { break; } } if (is_h) { sum += bessel_y(v, z); } else { sum += bessel_i(v, z); } /* * This error estimate is strictly speaking valid only for * n > v - 0.5, but numerical results indicate that it works * reasonably. */ *err = fabs(term) + fabs(maxterm) * 1e-16; return sum; }
/* * Power series for Struve H and L * http://dlmf.nist.gov/11.2.1 * * Starts to converge roughly at |n| > |z| */ double struve_power_series(double v, double z, int is_h, double *err) { int n, sgn; double term, sum, maxterm, scaleexp, tmp; double2_t cterm, csum, cdiv, z2, c2v, ctmp, ctmp2; if (is_h) { sgn = -1; } else { sgn = 1; } tmp = -lgam(v + 1.5) + (v + 1)*log(z/2); if (tmp < -600 || tmp > 600) { /* Scale exponent to postpone underflow/overflow */ scaleexp = tmp/2; tmp -= scaleexp; } else { scaleexp = 0; } term = 2 / sqrt(M_PI) * exp(tmp) * gammasgn(v + 1.5); sum = term; maxterm = 0; double2_init(&cterm, term); double2_init(&csum, sum); double2_init(&z2, sgn*z*z); double2_init(&c2v, 2*v); for (n = 0; n < MAXITER; ++n) { /* cdiv = (3 + 2*n) * (3 + 2*n + 2*v)) */ double2_init(&cdiv, 3 + 2*n); double2_init(&ctmp, 3 + 2*n); double2_add(&ctmp, &c2v, &ctmp); double2_mul(&cdiv, &ctmp, &cdiv); /* cterm *= z2 / cdiv */ double2_mul(&cterm, &z2, &cterm); double2_div(&cterm, &cdiv, &cterm); double2_add(&csum, &cterm, &csum); term = double2_double(&cterm); sum = double2_double(&csum); if (fabs(term) > maxterm) { maxterm = fabs(term); } if (fabs(term) < SUM_TINY * fabs(sum) || term == 0 || !npy_isfinite(sum)) { break; } } *err = fabs(term) + fabs(maxterm) * 1e-22; if (scaleexp != 0) { sum *= exp(scaleexp); *err *= exp(scaleexp); } if (sum == 0 && term == 0 && v < 0 && !is_h) { /* Spurious underflow */ *err = NPY_INFINITY; return NPY_NAN; } return sum; }
double lgam(double x) { double p, q, u, w, z; int i; sgngam = 1; if (!npy_isfinite(x)) return x; if (x < -34.0) { q = -x; w = lgam(q); /* note this modifies sgngam! */ p = floor(q); if (p == q) { lgsing: mtherr("lgam", SING); return (NPY_INFINITY); } i = p; if ((i & 1) == 0) sgngam = -1; else sgngam = 1; z = q - p; if (z > 0.5) { p += 1.0; z = p - q; } z = q * sin(NPY_PI * z); if (z == 0.0) goto lgsing; /* z = log(NPY_PI) - log( z ) - w; */ z = LOGPI - log(z) - w; return (z); } if (x < 13.0) { z = 1.0; p = 0.0; u = x; while (u >= 3.0) { p -= 1.0; u = x + p; z *= u; } while (u < 2.0) { if (u == 0.0) goto lgsing; z /= u; p += 1.0; u = x + p; } if (z < 0.0) { sgngam = -1; z = -z; } else sgngam = 1; if (u == 2.0) return (log(z)); p -= 2.0; x = x + p; p = x * polevl(x, B, 5) / p1evl(x, C, 6); return (log(z) + p); } if (x > MAXLGM) { return (sgngam * NPY_INFINITY); } q = (x - 0.5) * log(x) - x + LS2PI; if (x > 1.0e8) return (q); p = 1.0 / (x * x); if (x >= 1000.0) q += ((7.9365079365079365079365e-4 * p - 2.7777777777777777777778e-3) * p + 0.0833333333333333333333) / x; else q += polevl(p, A, 4) / x; return (q); }
double Gamma(double x) { double p, q, z; int i; sgngam = 1; if (!npy_isfinite(x)) { return x; } q = fabs(x); if (q > 33.0) { if (x < 0.0) { p = floor(q); if (p == q) { gamnan: mtherr("Gamma", OVERFLOW); return (NPY_INFINITY); } i = p; if ((i & 1) == 0) sgngam = -1; z = q - p; if (z > 0.5) { p += 1.0; z = q - p; } z = q * sin(NPY_PI * z); if (z == 0.0) { return (sgngam * NPY_INFINITY); } z = fabs(z); z = NPY_PI / (z * stirf(q)); } else { z = stirf(x); } return (sgngam * z); } z = 1.0; while (x >= 3.0) { x -= 1.0; z *= x; } while (x < 0.0) { if (x > -1.E-9) goto small; z /= x; x += 1.0; } while (x < 2.0) { if (x < 1.e-9) goto small; z /= x; x += 1.0; } if (x == 2.0) return (z); x -= 2.0; p = polevl(x, P, 6); q = polevl(x, Q, 7); return (z * p / q); small: if (x == 0.0) { goto gamnan; } else return (z / ((1.0 + 0.5772156649015329 * x) * x)); }