def _calc_spouge_coefficients(a, prec): """ Calculate Spouge coefficients for approximation with parameter a. Return a list of big integers representing the coefficients in fixed-point form with a precision of prec bits. """ # We'll store the coefficients as fixed-point numbers but calculate # them as Floats for convenience. The initial terms are huge, so we # need to allocate extra bits to ensure full accuracy. The integer # part of the largest term has size ~= exp(a) or 2**(1.4*a) floatprec = prec + int(a*1.4) Float.store() Float.setprec(floatprec) c = [0] * a b = exp(a-1) e = exp(1) c[0] = make_fixed(sqrt(2*pi_float()), prec) for k in range(1, a): # print "%02f" % (100.0 * k / a), "% done" c[k] = make_fixed(((-1)**(k-1) * (a-k)**k) * b / sqrt(a-k), prec) # Divide off e and k instead of computing exp and k! from scratch b = b / (e * k) Float.revert() return c
def cos_sin(x): """ cos_sin(x) calculates both the cosine and the sine of x rounded to the nearest Float value, and returns the tuple (cos(x), sin(x)). """ if not isinstance(x, Float): x = Float(x) bits_from_unit = abs(bitcount(x.man) + x.exp) prec = Float._prec + bits_from_unit + 15 xf = make_fixed(x, prec) n, rx = _trig_reduce(xf, prec) case = n % 4 one = 1 << prec if case == 0: s = _sin_series(rx, prec) c = _sqrt_fixed(one - ((s * s) >> prec), prec) elif case == 1: c = -_sin_series(rx, prec) s = _sqrt_fixed(one - ((c * c) >> prec), prec) elif case == 2: s = -_sin_series(rx, prec) c = -_sqrt_fixed(one - ((s * s) >> prec), prec) elif case == 3: c = _sin_series(rx, prec) s = -_sqrt_fixed(one - ((c * c) >> prec), prec) return Float((c, -prec)), Float((s, -prec))
def _lower_gamma_series(are, aim, zre, zim, prec): are = make_fixed(are, prec) aim = make_fixed(aim, prec) zre = make_fixed(zre, prec) zim = make_fixed(zim, prec) one = 1 << prec cre = sre = one cim = sim = 0 while abs(cre) > 3 or abs(cim) > 3: # c = (c * z) << prec cre, cim = (cre*zre-cim*zim)>>prec, (cim*zre+cre*zim)>>prec # c = c / (a+k) are += one mag = ((are**2 + aim**2) >> prec) cre, cim = (cre*are + cim*aim)//mag, (cim*are - cre*aim)//mag sre += cre sim += cim #k += 1 sre = Float((sre, -prec)) sim = Float((sim, -prec)) return ComplexFloat(sre, sim)
def _spouge_sum(x, prec, a, c): if isinstance(x, Float): # Regular fixed-point summation x = make_fixed(x, prec) s = c[0] for k in xrange(1, a): s += (c[k] << prec) // (x + (k << prec)) return Float((s, -prec)) elif isinstance(x, (Rational, int, long)): # Here we can save some work if isinstance(x, (int, long)): p, q = x, 1 else: p, q = x.p, x.q s = c[0] for k in xrange(1, a): s += c[k] * q // (p+q*k) return Float((s, -prec)) elif isinstance(x, ComplexFloat): """ For a complex number a + b*I, we have c_k (a+k)*c_k b * c_k ------------- = --------- - ------- * I (a + b*I) + k M M 2 2 2 2 2 where M = (a+k) + b = (a + b ) + (2*a*k + k ) """ re = make_fixed(x.real, prec) im = make_fixed(x.imag, prec) sre, sim = c[0], 0 mag = ((re**2)>>prec) + ((im**2)>>prec) for k in xrange(1, a): M = mag + re*(2*k) + ((k**2) << prec) sre += (c[k] * (re + (k << prec))) // M sim -= (c[k] * im) // M return ComplexFloat(Float((sre, -prec)), Float((sim, -prec)))
def _atan_series_2(x): prec = Float._prec prec = prec + 15 x = make_fixed(x, prec) one = 1 << prec x2 = (x * x) >> prec y = (x2 << prec) // (one + x2) s = a = one for n in xrange(1, 1000000): a = ((a * y) >> prec) * (2 * n) // (2 * n + 1) if a < 100: break s += a return Float(((y * s) // x, -prec))
def _atan_series_1(x): prec = Float._prec # Increase absolute precision when extremely close to 0 bc = bitcount(x.man) diff = -(bc + x.exp) if diff > 10: if 3 * diff - 4 > prec: # x**3 term vanishes; atan(x) ~x return +x prec = prec + diff prec += 15 # XXX: better estimate for number of guard bits x = make_fixed(x, prec) x2 = (x * x) >> prec one = 1 << prec s = a = x for n in xrange(1, 1000000): a = (a * x2) >> prec s += a // ((-1) ** n * (n + n + 1)) if -100 < a < 100: break return Float((s, -prec))
def exp(x): """ exp(x) -- compute the exponential function of the real or complex number x """ if isinstance(x, (ComplexFloat, complex)): mag = exp(x.real) re, im = cos_sin(x.imag) return ComplexFloat(mag * re, mag * im) else: if not isinstance(x, Float): x = Float(x) # extra precision needs to be similar in magnitude to log_2(|x|) prec = Float._prec + 4 + max(0, bitcount(x.man) + x.exp) t = make_fixed(x, prec) if abs(x) > 1: lg2 = log2_fixed(prec) n, t = divmod(t, lg2) else: n = 0 y = _exp_series(t, prec) return Float((y, -prec + n))