def mpf_gamma_int(n, prec, rounding=round_fast): if n < 1000: return from_int(int_fac(n - 1), prec, rounding) # XXX: choose the cutoff less arbitrarily size = int(n * math.log(n, 2)) if prec > size / 20.0: return from_int(int_fac(n - 1), prec, rounding) return mpf_gamma(from_int(n), prec, rounding)
def mpf_gamma(x, prec, rounding=round_fast, p1=1): """ Computes the gamma function of a real floating-point argument. With p1=0, computes a factorial instead. """ sign, man, exp, bc = x if not man: if x == finf: return finf if x == fninf or x == fnan: return fnan # More precision is needed for enormous x. TODO: # use Stirling's formula + Euler-Maclaurin summation size = exp + bc if size > 5: size = int(size * math.log(size, 2)) wp = prec + max(0, size) + 15 if exp >= 0: if sign or (p1 and not man): raise ValueError("gamma function pole") # A direct factorial is fastest if exp + bc <= 10: return from_int(int_fac((man << exp) - p1), prec, rounding) reflect = sign or exp + bc < -1 if p1: # Should be done exactly! x = mpf_sub(x, fone) # x < 0.25 if reflect: # gamma = pi / (sin(pi*x) * gamma(1-x)) wp += 15 pix = mpf_mul(x, mpf_pi(wp), wp) t = mpf_sin_pi(x, wp) g = mpf_gamma(mpf_sub(fone, x), wp) return mpf_div(pix, mpf_mul(t, g, wp), prec, rounding) sprec, a, c = get_spouge_coefficients(wp) s = spouge_sum_real(x, sprec, a, c) # gamma = exp(log(x+a)*(x+0.5) - xpa) * s xpa = mpf_add(x, from_int(a), wp) logxpa = mpf_log(xpa, wp) xph = mpf_add(x, fhalf, wp) t = mpf_sub(mpf_mul(logxpa, xph, wp), xpa, wp) t = mpf_mul(mpf_exp(t, wp), s, prec, rounding) return t