void fpoly_bessel(unsigned int _n, float * _p) { unsigned int k; unsigned int N = _n-1; for (k=0; k<_n; k++) { #if 0 // use internal log(gamma(z)) float t0 = liquid_lngammaf((float)(2*N-k)+1); float t1 = liquid_lngammaf((float)(N-k) +1); float t2 = liquid_lngammaf((float)(k) +1); #else // use standard math log(gamma(z)) float t0 = lgammaf((float)(2*N-k)+1); float t1 = lgammaf((float)(N-k) +1); float t2 = lgammaf((float)(k) +1); #endif // M_LN2 = log(2) = 0.693147180559945 float t3 = M_LN2 * (float)(N-k); // log(2^(N-k)) = log(2)*log(N-k) _p[k] = roundf(expf(t0 - t1 - t2 - t3)); #if 0 printf(" p[%3u,%3u] = %12.4e\n", k, _n, _p[k]); printf(" t0 : %12.4e\n", t0); printf(" t1 : %12.4e\n", t1); printf(" t2 : %12.4e\n", t2); printf(" t3 : %12.4e\n", t3); #endif } }
float liquid_lngammaf(float _z) { float g; if (_z < 0) { fprintf(stderr,"error: liquid_lngammaf(), undefined for z <= 0\n"); exit(1); } else if (_z < 10.0f) { #if 0 g = -EULER_GAMMA*_z - logf(_z); unsigned int k; for (k=1; k<NUM_LNGAMMA_ITERATIONS; k++) { float t0 = _z / (float)k; float t1 = logf(1.0f + t0); g += t0 - t1; } #else // Use recursive formula: // gamma(z+1) = z * gamma(z) // therefore: // log(Gamma(z)) = log(gamma(z+1)) - ln(z) return liquid_lngammaf(_z + 1.0f) - logf(_z); #endif } else { // high value approximation g = 0.5*( logf(2*M_PI)-log(_z) ); g += _z*( logf(_z+(1/(12.0f*_z-0.1f/_z)))-1); } return g; }
float liquid_lnlowergammaf(float _z, float _alpha) { float t0 = _z * logf(_alpha); float t1 = liquid_lngammaf(_z); float t2 = -_alpha; float t3 = 0.0f; unsigned int k = 0; float log_alpha = logf(_alpha); float tprime = 0.0f; float tmax = 0.0f; float t = 0.0f; for (k=0; k<LOWERGAMMA_MAX_ITERATIONS; k++) { // retain previous value for t tprime = t; // compute log( alpha^k / Gamma(_z + k + 1) ) // = k*log(alpha) - lnGamma(_z + k + 1) t = k*log_alpha - liquid_lngammaf(_z + (float)k + 1.0f); // accumulate e^t t3 += expf(t); // check premature exit criteria if (k==0 || t > tmax) tmax = t; // conditions: // 1. minimum number of iterations met // 2. surpassed inflection point: k*log(alpha) - log(Gamma(z+k+1)) // has an inverted parabolic shape // 3. sufficiently beyond peak if ( k > LOWERGAMMA_MIN_ITERATIONS && tprime > t && (tmax-t) > 20.0f) break; } return t0 + t1 + t2 + logf(t3); }
float liquid_gammaf(float _z) { if (_z < 0) { // use identities // (1) gamma(z)*gamma(-z) = -pi / (z*sin(pi*z)) // (2) z*gamma(z) = gamma(1+z) // // therefore: // gamma(z) = pi / ( gamma(1-z) * sin(pi*z) ) float t0 = liquid_gammaf(1.0 - _z); float t1 = sinf(M_PI*_z); if (t0==0 || t1==0) fprintf(stderr,"warning: liquid_gammaf(), divide by zero\n"); return M_PI / (t0 * t1); } else { return expf( liquid_lngammaf(_z) ); } }
// // AUTOTEST: lngamma // void autotest_lngamma() { // error tolerance float tol = 1e-4f; // test vectors CONTEND_DELTA( liquid_lngammaf(1.00000000000000e-05), 1.15129196928958e+01, tol ); CONTEND_DELTA( liquid_lngammaf(1.47910838816821e-05), 1.11214774616959e+01, tol ); CONTEND_DELTA( liquid_lngammaf(2.18776162394955e-05), 1.07300339056431e+01, tol ); CONTEND_DELTA( liquid_lngammaf(3.23593656929628e-05), 1.03385883900717e+01, tol ); CONTEND_DELTA( liquid_lngammaf(4.78630092322638e-05), 9.94713997633970e+00, tol ); CONTEND_DELTA( liquid_lngammaf(7.07945784384137e-05), 9.55568727630758e+00, tol ); CONTEND_DELTA( liquid_lngammaf(1.04712854805090e-04), 9.16422823723390e+00, tol ); CONTEND_DELTA( liquid_lngammaf(1.54881661891248e-04), 8.77275982391398e+00, tol ); CONTEND_DELTA( liquid_lngammaf(2.29086765276777e-04), 8.38127754918765e+00, tol ); CONTEND_DELTA( liquid_lngammaf(3.38844156139203e-04), 7.98977478095072e+00, tol ); CONTEND_DELTA( liquid_lngammaf(5.01187233627273e-04), 7.59824172030200e+00, tol ); CONTEND_DELTA( liquid_lngammaf(7.41310241300918e-04), 7.20666389700363e+00, tol ); CONTEND_DELTA( liquid_lngammaf(1.09647819614319e-03), 6.81501995916639e+00, tol ); CONTEND_DELTA( liquid_lngammaf(1.62181009735893e-03), 6.42327843686104e+00, tol ); CONTEND_DELTA( liquid_lngammaf(2.39883291901949e-03), 6.03139302698778e+00, tol ); CONTEND_DELTA( liquid_lngammaf(3.54813389233576e-03), 5.63929577576287e+00, tol ); CONTEND_DELTA( liquid_lngammaf(5.24807460249773e-03), 5.24688733606614e+00, tol ); CONTEND_DELTA( liquid_lngammaf(7.76247116628692e-03), 4.85402329836329e+00, tol ); CONTEND_DELTA( liquid_lngammaf(1.14815362149688e-02), 4.46049557831785e+00, tol ); CONTEND_DELTA( liquid_lngammaf(1.69824365246175e-02), 4.06600834803635e+00, tol ); CONTEND_DELTA( liquid_lngammaf(2.51188643150958e-02), 3.67014984368726e+00, tol ); CONTEND_DELTA( liquid_lngammaf(3.71535229097173e-02), 3.27236635981559e+00, tol ); CONTEND_DELTA( liquid_lngammaf(5.49540873857625e-02), 2.87195653880158e+00, tol ); CONTEND_DELTA( liquid_lngammaf(8.12830516164099e-02), 2.46812982675138e+00, tol ); CONTEND_DELTA( liquid_lngammaf(1.20226443461741e-01), 2.06022544058646e+00, tol ); CONTEND_DELTA( liquid_lngammaf(1.77827941003892e-01), 1.64828757901128e+00, tol ); CONTEND_DELTA( liquid_lngammaf(2.63026799189538e-01), 1.23436563201614e+00, tol ); CONTEND_DELTA( liquid_lngammaf(3.89045144994281e-01), 8.25181176502332e-01, tol ); CONTEND_DELTA( liquid_lngammaf(5.75439937337158e-01), 4.37193579132034e-01, tol ); CONTEND_DELTA( liquid_lngammaf(8.51138038202378e-01), 1.05623142071343e-01, tol ); CONTEND_DELTA( liquid_lngammaf(1.25892541179417e+00),-1.00254418080515e-01, tol ); CONTEND_DELTA( liquid_lngammaf(1.86208713666287e+00),-5.19895823734552e-02, tol ); CONTEND_DELTA( liquid_lngammaf(2.75422870333817e+00), 4.78681466346387e-01, tol ); CONTEND_DELTA( liquid_lngammaf(4.07380277804113e+00), 1.88523210546678e+00, tol ); CONTEND_DELTA( liquid_lngammaf(6.02559586074358e+00), 4.83122059829788e+00, tol ); CONTEND_DELTA( liquid_lngammaf(8.91250938133746e+00), 1.04177681572532e+01, tol ); CONTEND_DELTA( liquid_lngammaf(1.31825673855641e+01), 2.04497048921129e+01, tol ); CONTEND_DELTA( liquid_lngammaf(1.94984459975805e+01), 3.78565107279246e+01, tol ); CONTEND_DELTA( liquid_lngammaf(2.88403150312661e+01), 6.73552537656878e+01, tol ); CONTEND_DELTA( liquid_lngammaf(4.26579518801593e+01), 1.16490742768456e+02, tol ); CONTEND_DELTA( liquid_lngammaf(6.30957344480194e+01), 1.97262133863497e+02, tol ); CONTEND_DELTA( liquid_lngammaf(9.33254300796992e+01), 3.28659150940827e+02, tol ); CONTEND_DELTA( liquid_lngammaf(1.38038426460289e+02), 5.40606126998515e+02, tol ); // test very large numbers CONTEND_DELTA( liquid_lngammaf(140), 550.278651724286, tol); CONTEND_DELTA( liquid_lngammaf(150), 600.009470555327, tol); CONTEND_DELTA( liquid_lngammaf(160), 650.409682895655, tol); CONTEND_DELTA( liquid_lngammaf(170), 701.437263808737, tol); }