def mpf_rdiv_int(n, t, prec, rnd=round_fast): """Floating-point division n/t with a Python integer as numerator""" sign, man, exp, bc = t if not n or not man: return mpf_div(from_int(n), t, prec, rnd) if n < 0: sign ^= 1 n = -n extra = prec + bc + 5 quot, rem = divmod(n<<extra, man) if rem: quot = (quot<<1) + 1 extra += 1 return normalize1(sign, quot, -exp-extra, bitcount(quot), prec, rnd) return normalize(sign, quot, -exp-extra, bitcount(quot), prec, rnd)
def from_man_exp(man, exp, prec=None, rnd=round_fast): """Create raw mpf from (man, exp) pair. The mantissa may be signed. If no precision is specified, the mantissa is stored exactly.""" man = MP_BASE(man) sign = 0 if man < 0: sign = 1 man = -man if man < 1024: bc = bctable[int(man)] else: bc = bitcount(man) if not prec: if not man: return fzero if not man & 1: if man & 2: return (sign, man >> 1, exp + 1, bc - 1) t = trailtable[int(man & 255)] if not t: while not man & 255: man >>= 8 exp += 8 bc -= 8 t = trailtable[int(man & 255)] man >>= t exp += t bc -= t return (sign, man, exp, bc) return normalize(sign, man, exp, bc, prec, rnd)
def strict_normalize(sign, man, exp, bc, prec, rnd): """Additional checks on the components of an mpf. Enable tests by setting the environment variable MPMATH_STRICT to Y.""" assert type(man) == MP_BASE_TYPE assert type(bc) in (int, long) assert type(exp) in (int, long) assert bc == bitcount(man) return _normalize(sign, man, exp, bc, prec, rnd)
def from_bstr(x): man, exp = str_to_man_exp(x, base=2) man = MP_BASE(man) sign = 0 if man < 0: man = -man sign = 1 bc = bitcount(man) return normalize(sign, man, exp, bc, bc, round_floor)
def gmpy_mpf_mul_int(s, n, prec, rnd=round_fast): """Multiply by a Python integer.""" sign, man, exp, bc = s if not man: return mpf_mul(s, from_int(n), prec, rnd) if not n: return fzero if n < 0: sign ^= 1 n = -n man *= n return normalize(sign, man, exp, bitcount(man), prec, rnd)
def to_digits_exp(s, dps): """Helper function for representing the floating-point number s as a decimal with dps digits. Returns (sign, string, exponent) where sign is '' or '-', string is the digit string, and exponent is the decimal exponent as an int. If inexact, the decimal representation is rounded toward zero.""" # Extract sign first so it doesn't mess up the string digit count if s[0]: sign = '-' s = mpf_neg(s) else: sign = '' _sign, man, exp, bc = s if not man: return '', '0', 0 bitprec = int(dps * math.log(10,2)) + 10 # Cut down to size # TODO: account for precision when doing this exp_from_1 = exp + bc if abs(exp_from_1) > 3500: from libelefun import mpf_ln2, mpf_ln10 # Set b = int(exp * log(2)/log(10)) # If exp is huge, we must use high-precision arithmetic to # find the nearest power of ten expprec = bitcount(abs(exp)) + 5 tmp = from_int(exp) tmp = mpf_mul(tmp, mpf_ln2(expprec)) tmp = mpf_div(tmp, mpf_ln10(expprec), expprec) b = to_int(tmp) s = mpf_div(s, mpf_pow_int(ften, b, bitprec), bitprec) _sign, man, exp, bc = s exponent = b else: exponent = 0 # First, calculate mantissa digits by converting to a binary # fixed-point number and then converting that number to # a decimal fixed-point number. fixprec = max(bitprec - exp - bc, 0) fixdps = int(fixprec / math.log(10,2) + 0.5) sf = to_fixed(s, fixprec) sd = bin_to_radix(sf, fixprec, 10, fixdps) digits = numeral(sd, base=10, size=dps) exponent += len(digits) - fixdps - 1 return sign, digits, exponent
def mpf_div(s, t, prec, rnd=round_fast): """Floating-point division""" ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t if not sman or not tman: if s == fzero: if t == fzero: raise ZeroDivisionError if t == fnan: return fnan return fzero if t == fzero: raise ZeroDivisionError s_special = (not sman) and sexp t_special = (not tman) and texp if s_special and t_special: return fnan if s == fnan or t == fnan: return fnan if not t_special: if t == fzero: return fnan return {1: finf, -1: fninf}[mpf_sign(s) * mpf_sign(t)] return fzero sign = ssign ^ tsign if tman == 1: return normalize1(sign, sman, sexp - texp, sbc, prec, rnd) # Same strategy as for addition: if there is a remainder, perturb # the result a few bits outside the precision range before rounding extra = prec - sbc + tbc + 5 if extra < 5: extra = 5 quot, rem = divmod(sman << extra, tman) if rem: quot = (quot << 1) + 1 extra += 1 return normalize1(sign, quot, sexp - texp - extra, bitcount(quot), prec, rnd) return normalize(sign, quot, sexp - texp - extra, bitcount(quot), prec, rnd)
def mpf_div(s, t, prec, rnd=round_fast): """Floating-point division""" ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t if not sman or not tman: if s == fzero: if t == fzero: raise ZeroDivisionError if t == fnan: return fnan return fzero if t == fzero: raise ZeroDivisionError s_special = (not sman) and sexp t_special = (not tman) and texp if s_special and t_special: return fnan if s == fnan or t == fnan: return fnan if not t_special: if t == fzero: return fnan return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] return fzero sign = ssign ^ tsign if tman == 1: return normalize1(sign, sman, sexp-texp, sbc, prec, rnd) # Same strategy as for addition: if there is a remainder, perturb # the result a few bits outside the precision range before rounding extra = prec - sbc + tbc + 5 if extra < 5: extra = 5 quot, rem = divmod(sman<<extra, tman) if rem: quot = (quot<<1) + 1 extra += 1 return normalize1(sign, quot, sexp-texp-extra, bitcount(quot), prec, rnd) return normalize(sign, quot, sexp-texp-extra, bitcount(quot), prec, rnd)
def mpf_sum(xs, prec=0, rnd=round_fast, absolute=False): """ Sum a list of mpf values efficiently and accurately (typically no temporary roundoff occurs). If prec=0, the final result will not be rounded either. There may be roundoff error or cancellation if extremely large exponent differences occur. With absolute=True, sums the absolute values. """ man = 0 exp = 0 max_extra_prec = prec*2 or 1000000 # XXX special = None for x in xs: xsign, xman, xexp, xbc = x if xman: if xsign and not absolute: xman = -xman delta = xexp - exp if xexp >= exp: # x much larger than existing sum? # first: quick test if (delta > max_extra_prec) and \ ((not man) or delta-bitcount(abs(man)) > max_extra_prec): man = xman exp = xexp else: man += (xman << delta) else: delta = -delta # x much smaller than existing sum? if delta-xbc > max_extra_prec: if not man: man, exp = xman, xexp else: man = (man << delta) + xman exp = xexp elif xexp: if absolute: x = mpf_abs(x) special = mpf_add(special or fzero, x, 1) # Will be inf or nan if special: return special return from_man_exp(man, exp, prec, rnd)
def python_mpf_mul_int(s, n, prec, rnd=round_fast): """Multiply by a Python integer.""" sign, man, exp, bc = s if not man: return mpf_mul(s, from_int(n), prec, rnd) if not n: return fzero if n < 0: sign ^= 1 n = -n man *= n # Generally n will be small if n < 1024: bc += bctable[int(n)] - 1 else: bc += bitcount(n) - 1 bc += int(man>>bc) return normalize(sign, man, exp, bc, prec, rnd)
def gmpy_mpf_mul(s, t, prec=0, rnd=round_fast): """Multiply two raw mpfs""" ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t sign = ssign ^ tsign man = sman*tman if man: bc = bitcount(man) if prec: return normalize1(sign, man, sexp+texp, bc, prec, rnd) else: return (sign, man, sexp+texp, bc) s_special = (not sman) and sexp t_special = (not tman) and texp if not s_special and not t_special: return fzero if fnan in (s, t): return fnan if (not tman) and texp: s, t = t, s if t == fzero: return fnan return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)]
def mpf_mod(s, t, prec, rnd=round_fast): ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t if ((not sman) and sexp) or ((not tman) and texp): return fnan # Important special case: do nothing if t is larger if ssign == tsign and texp > sexp+sbc: return s # Another important special case: this allows us to do e.g. x % 1.0 # to find the fractional part of x, and it will work when x is huge. if tman == 1 and sexp > texp+tbc: return fzero base = min(sexp, texp) sman = (-1)**ssign * sman tman = (-1)**tsign * tman man = (sman << (sexp-base)) % (tman << (texp-base)) if man >= 0: sign = 0 else: man = -man sign = 1 return normalize(sign, man, base, bitcount(man), prec, rnd)
def mpf_pow_int(s, n, prec, rnd=round_fast): """Compute s**n, where s is a raw mpf and n is a Python integer.""" sign, man, exp, bc = s if (not man) and exp: if s == finf: if n > 0: return s if n == 0: return fnan return fzero if s == fninf: if n > 0: return [finf, fninf][n & 1] if n == 0: return fnan return fzero return fnan n = int(n) if n == 0: return fone if n == 1: return mpf_pos(s, prec, rnd) if n == 2: _, man, exp, bc = s if not man: return fzero man = man*man if man == 1: return (0, MP_ONE, exp+exp, 1) bc = bc + bc - 2 bc += bctable[int(man>>bc)] return normalize1(0, man, exp+exp, bc, prec, rnd) if n == -1: return mpf_div(fone, s, prec, rnd) if n < 0: inverse = mpf_pow_int(s, -n, prec+5, reciprocal_rnd[rnd]) return mpf_div(fone, inverse, prec, rnd) result_sign = sign & n # Use exact integer power when the exact mantissa is small if man == 1: return (result_sign, MP_ONE, exp*n, 1) if bc*n < 1000: man **= n return normalize1(result_sign, man, exp*n, bitcount(man), prec, rnd) # Use directed rounding all the way through to maintain rigorous # bounds for interval arithmetic rounds_down = (rnd is round_nearest) or \ shifts_down[rnd][result_sign] # Now we perform binary exponentiation. Need to estimate precision # to avoid rounding errors from temporary operations. Roughly log_2(n) # operations are performed. workprec = prec + 4*bitcount(n) + 4 _, pm, pe, pbc = fone while 1: if n & 1: pm = pm*man pe = pe+exp pbc += bc - 2 pbc = pbc + bctable[int(pm >> pbc)] if pbc > workprec: if rounds_down: pm = pm >> (pbc-workprec) else: pm = -((-pm) >> (pbc-workprec)) pe += pbc - workprec pbc = workprec n -= 1 if not n: break man = man*man exp = exp+exp bc = bc + bc - 2 bc = bc + bctable[int(man >> bc)] if bc > workprec: if rounds_down: man = man >> (bc-workprec) else: man = -((-man) >> (bc-workprec)) exp += bc - workprec bc = workprec n = n // 2 return normalize(result_sign, pm, pe, pbc, prec, rnd)
def to_bstr(x): sign, man, exp, bc = x return ['','-'][sign] + numeral(man, size=bitcount(man), base=2) + ("e%i" % exp)
def mpf_add(s, t, prec=0, rnd=round_fast, _sub=0): """ Add the two raw mpf values s and t. With prec=0, no rounding is performed. Note that this can produce a very large mantissa (potentially too large to fit in memory) if exponents are far apart. """ ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t tsign ^= _sub # Standard case: two nonzero, regular numbers if sman and tman: offset = sexp - texp if offset: if offset > 0: # Outside precision range; only need to perturb if offset > 100 and prec: delta = sbc + sexp - tbc - texp if delta > prec + 4: offset = prec + 4 sman <<= offset if tsign: sman -= 1 else: sman += 1 return normalize1(ssign, sman, sexp-offset, bitcount(sman), prec, rnd) # Add if ssign == tsign: man = tman + (sman << offset) # Subtract else: if ssign: man = tman - (sman << offset) else: man = (sman << offset) - tman if man >= 0: ssign = 0 else: man = -man ssign = 1 bc = bitcount(man) return normalize1(ssign, man, texp, bc, prec or bc, rnd) elif offset < 0: # Outside precision range; only need to perturb if offset < -100 and prec: delta = tbc + texp - sbc - sexp if delta > prec + 4: offset = prec + 4 tman <<= offset if ssign: tman -= 1 else: tman += 1 return normalize1(tsign, tman, texp-offset, bitcount(tman), prec, rnd) # Add if ssign == tsign: man = sman + (tman << -offset) # Subtract else: if tsign: man = sman - (tman << -offset) else: man = (tman << -offset) - sman if man >= 0: ssign = 0 else: man = -man ssign = 1 bc = bitcount(man) return normalize1(ssign, man, sexp, bc, prec or bc, rnd) # Equal exponents; no shifting necessary if ssign == tsign: man = tman + sman else: if ssign: man = tman - sman else: man = sman - tman if man >= 0: ssign = 0 else: man = -man ssign = 1 bc = bitcount(man) return normalize(ssign, man, texp, bc, prec or bc, rnd) # Handle zeros and special numbers if _sub: t = mpf_neg(t) if not sman: if sexp: if s == t or tman or not texp: return s return fnan if tman: return normalize1(tsign, tman, texp, tbc, prec or tbc, rnd) return t if texp: return t if sman: return normalize1(ssign, sman, sexp, sbc, prec or sbc, rnd) return s