Exemple #1
0
def mpf_asin(x, prec, rnd=round_fast):
    sign, man, exp, bc = x
    if bc+exp > 0 and x not in (fone, fnone):
        raise ComplexResult("asin(x) is real only for -1 <= x <= 1")
    flag_nr = True
    if prec < 1000 or exp+bc < -13:
        flag_nr = False
    else:
        ebc = exp + bc
        if ebc < -13:
            flag_nr = False
        elif ebc < -3:
            if prec < 3000:
                flag_nr = False
    if not flag_nr:
        # asin(x) = 2*atan(x/(1+sqrt(1-x**2)))
        wp = prec + 15
        a = mpf_mul(x, x)
        b = mpf_add(fone, mpf_sqrt(mpf_sub(fone, a, wp), wp), wp)
        c = mpf_div(x, b, wp)
        return mpf_shift(mpf_atan(c, prec, rnd), 1)
    # use Newton's method
    extra = 10
    extra_p = 10
    prec2 = prec + extra
    r = math.asin(to_float(x))
    r = from_float(r, 50, rnd)
    for p in giant_steps(50, prec2):
        wp = p + extra_p
        c, s = cos_sin(r, wp, rnd)
        tmp = mpf_sub(x, s, wp, rnd)
        tmp = mpf_div(tmp, c, wp, rnd)
        r = mpf_add(r, tmp, wp, rnd)
    sign, man, exp, bc = r
    return normalize(sign, man, exp, bc, prec, rnd)
Exemple #2
0
def mpf_asin(x, prec, rnd=round_fast):
    sign, man, exp, bc = x
    if bc+exp > 0 and x not in (fone, fnone):
        raise ComplexResult("asin(x) is real only for -1 <= x <= 1")
    flag_nr = True
    if prec < 1000 or exp+bc < -13:
        flag_nr = False
    else:
        ebc = exp + bc
        if ebc < -13:
            flag_nr = False
        elif ebc < -3:
            if prec < 3000:
                flag_nr = False
    if not flag_nr:
        # asin(x) = 2*atan(x/(1+sqrt(1-x**2)))
        wp = prec + 15
        a = mpf_mul(x, x)
        b = mpf_add(fone, mpf_sqrt(mpf_sub(fone, a, wp), wp), wp)
        c = mpf_div(x, b, wp)
        return mpf_shift(mpf_atan(c, prec, rnd), 1)
    # use Newton's method
    extra = 10
    extra_p = 10
    prec2 = prec + extra
    r = math.asin(to_float(x))
    r = from_float(r, 50, rnd)
    for p in giant_steps(50, prec2):
        wp = p + extra_p
        c, s = cos_sin(r, wp, rnd)
        tmp = mpf_sub(x, s, wp, rnd)
        tmp = mpf_div(tmp, c, wp, rnd)
        r = mpf_add(r, tmp, wp, rnd)
    sign, man, exp, bc = r
    return normalize(sign, man, exp, bc, prec, rnd)
Exemple #3
0
def mpc_nthroot(z, n, prec, rnd=round_fast):
    """
    Complex n-th root.

    Use Newton method as in the real case when it is faster,
    otherwise use z**(1/n)
    """
    a, b = z
    if a[0] == 0 and b == fzero:
        re = mpf_nthroot(a, n, prec, rnd)
        return (re, fzero)
    if n < 2:
        if n == 0:
            return mpc_one
        if n == 1:
            return mpc_pos((a, b), prec, rnd)
        if n == -1:
            return mpc_div(mpc_one, (a, b), prec, rnd)
        inverse = mpc_nthroot((a, b), -n, prec + 5, reciprocal_rnd[rnd])
        return mpc_div(mpc_one, inverse, prec, rnd)
    if n <= 20:
        prec2 = int(1.2 * (prec + 10))
        asign, aman, aexp, abc = a
        bsign, bman, bexp, bbc = b
        pf = mpc_abs((a, b), prec)
        if pf[-2] + pf[-1] > -10 and pf[-2] + pf[-1] < prec:
            af = to_fixed(a, prec2)
            bf = to_fixed(b, prec2)
            re, im = mpc_nthroot_fixed(af, bf, n, prec2)
            extra = 10
            re = from_man_exp(re, -prec2 - extra, prec2, rnd)
            im = from_man_exp(im, -prec2 - extra, prec2, rnd)
            return re, im
    fn = from_int(n)
    prec2 = prec + 10 + 10
    nth = mpf_rdiv_int(1, fn, prec2)
    re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd)
    re = normalize(re[0], re[1], re[2], re[3], prec, rnd)
    im = normalize(im[0], im[1], im[2], im[3], prec, rnd)
    return re, im
Exemple #4
0
def mpc_nthroot(z, n, prec, rnd=round_fast):
    """
    Complex n-th root.

    Use Newton method as in the real case when it is faster,
    otherwise use z**(1/n)
    """
    a, b = z
    if a[0] == 0 and b == fzero:
        re = mpf_nthroot(a, n, prec, rnd)
        return (re, fzero)
    if n < 2:
        if n == 0:
            return mpc_one
        if n == 1:
            return mpc_pos((a, b), prec, rnd)
        if n == -1:
            return mpc_div(mpc_one, (a, b), prec, rnd)
        inverse = mpc_nthroot((a, b), -n, prec+5, reciprocal_rnd[rnd])
        return mpc_div(mpc_one, inverse, prec, rnd)
    if n <= 20:
        prec2 = int(1.2 * (prec + 10))
        asign, aman, aexp, abc = a
        bsign, bman, bexp, bbc = b
        pf = mpc_abs((a,b), prec)
        if pf[-2] + pf[-1] > -10  and pf[-2] + pf[-1] < prec:
            af = to_fixed(a, prec2)
            bf = to_fixed(b, prec2)
            re, im = mpc_nthroot_fixed(af, bf, n, prec2)
            extra = 10
            re = from_man_exp(re, -prec2-extra, prec2, rnd)
            im = from_man_exp(im, -prec2-extra, prec2, rnd)
            return re, im
    fn = from_int(n)
    prec2 = prec+10 + 10
    nth = mpf_rdiv_int(1, fn, prec2)
    re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd)
    re = normalize(re[0], re[1], re[2], re[3], prec, rnd)
    im = normalize(im[0], im[1], im[2], im[3], prec, rnd)
    return re, im
Exemple #5
0
 def __new__(cls, val=fzero, **kwargs):
     """A new mpf can be created from a Python float, an int, a
     or a decimal string representing a number in floating-point
     format."""
     prec, rounding = prec_rounding
     if kwargs:
         prec = kwargs.get('prec', prec)
         if 'dps' in kwargs:
             prec = dps_to_prec(kwargs['dps'])
         rounding = kwargs.get('rounding', rounding)
     if type(val) is cls:
         sign, man, exp, bc = val._mpf_
         if (not man) and exp:
             return val
         return make_mpf(normalize(sign, man, exp, bc, prec, rounding))
     elif type(val) is tuple:
         if len(val) == 2:
             return make_mpf(from_man_exp(val[0], val[1], prec, rounding))
         if len(val) == 4:
             sign, man, exp, bc = val
             return make_mpf(normalize(sign, MP_BASE(man), exp, bc, prec, rounding))
         raise ValueError
     else:
         return make_mpf(mpf_pos(mpf_convert_arg(val, prec, rounding), prec, rounding))
Exemple #6
0
 def __new__(cls, val=fzero, **kwargs):
     """A new mpf can be created from a Python float, an int, a
     or a decimal string representing a number in floating-point
     format."""
     prec, rounding = prec_rounding
     if kwargs:
         prec = kwargs.get('prec', prec)
         if 'dps' in kwargs:
             prec = dps_to_prec(kwargs['dps'])
         rounding = kwargs.get('rounding', rounding)
     if type(val) is cls:
         sign, man, exp, bc = val._mpf_
         if (not man) and exp:
             return val
         return make_mpf(normalize(sign, man, exp, bc, prec, rounding))
     elif type(val) is tuple:
         if len(val) == 2:
             return make_mpf(from_man_exp(val[0], val[1], prec, rounding))
         if len(val) == 4:
             sign, man, exp, bc = val
             return make_mpf(normalize(sign, MP_BASE(man), exp, bc, prec, rounding))
         raise ValueError
     else:
         return make_mpf(mpf_pos(mpf_convert_arg(val, prec, rounding), prec, rounding))
Exemple #7
0
def mpf_exp(x, prec, rnd=round_fast):
    sign, man, exp, bc = x
    if not man:
        if not exp:
            return fone
        if x == fninf:
            return fzero
        return x
    # Fast handling e**n. TODO: the best cutoff depends on both the
    # size of n and the precision.
    if prec > 600 and exp >= 0:
        return mpf_pow_int(mpf_e(prec + 10), (-1)**sign * (man << exp), prec,
                           rnd)
    mag = bc + exp
    if mag < -prec - 10:
        return mpf_perturb(fone, sign, prec, rnd)
    # extra precision needs to be similar in magnitude to log_2(|x|)
    # for the modulo reduction, plus r for the error from squaring r times
    wp = prec + max(0, mag)
    if wp < 300:
        r = int(2 * wp**0.4)
        if mag < 0:
            r = max(1, r + mag)
        wp += r + 20
        t = to_fixed(x, wp)
        # abs(x) > 1?
        if mag > 1:
            lg2 = ln2_fixed(wp)
            n, t = divmod(t, lg2)
        else:
            n = 0
        man = exp_series(t, wp, r)
    else:
        r = int(0.7 * wp**0.5)
        if mag < 0:
            r = max(1, r + mag)
        wp += r + 20
        t = to_fixed(x, wp)
        if mag > 1:
            lg2 = ln2_fixed(wp)
            n, t = divmod(t, lg2)
        else:
            n = 0
        man = exp_series2(t, wp, r)
    bc = wp - 2 + bctable[int(man >> (wp - 2))]
    return normalize(0, man, int(-wp + n), bc, prec, rnd)
Exemple #8
0
def mpf_exp(x, prec, rnd=round_fast):
    sign, man, exp, bc = x
    if not man:
        if not exp:
            return fone
        if x == fninf:
            return fzero
        return x
    # Fast handling e**n. TODO: the best cutoff depends on both the
    # size of n and the precision.
    if prec > 600 and exp >= 0:
        return mpf_pow_int(mpf_e(prec+10), (-1)**sign *(man<<exp), prec, rnd)
    mag = bc+exp
    if mag < -prec-10:
        return mpf_perturb(fone, sign, prec, rnd)
    # extra precision needs to be similar in magnitude to log_2(|x|)
    # for the modulo reduction, plus r for the error from squaring r times
    wp = prec + max(0, mag)
    if wp < 300:
        r = int(2*wp**0.4)
        if mag < 0:
            r = max(1, r + mag)
        wp += r + 20
        t = to_fixed(x, wp)
        # abs(x) > 1?
        if mag > 1:
            lg2 = ln2_fixed(wp)
            n, t = divmod(t, lg2)
        else:
            n = 0
        man = exp_series(t, wp, r)
    else:
        r = int(0.7 * wp**0.5)
        if mag < 0:
            r = max(1, r + mag)
        wp += r + 20
        t = to_fixed(x, wp)
        if mag > 1:
            lg2 = ln2_fixed(wp)
            n, t = divmod(t, lg2)
        else:
            n = 0
        man = exp_series2(t, wp, r)
    bc = wp - 2 + bctable[int(man >> (wp - 2))]
    return normalize(0, man, int(-wp+n), bc, prec, rnd)
Exemple #9
0
def mpf_exp(x, prec, rnd=round_fast):
    sign, man, exp, bc = x
    if not man:
        if not exp:
            return fone
        if x == fninf:
            return fzero
        return x
    mag = bc+exp
    # Fast handling e**n. TODO: the best cutoff depends on both the
    # size of n and the precision.
    if prec > 600 and exp >= 0:
        e = mpf_e(prec+10+int(1.45*mag))
        return mpf_pow_int(e, (-1)**sign *(man<<exp), prec, rnd)
    if mag < -prec-10:
        return mpf_perturb(fone, sign, prec, rnd)
    # extra precision needs to be similar in magnitude to log_2(|x|)
    # for the modulo reduction, plus r for the error from squaring r times
    wp = prec + max(0, mag)
    if wp < 300:
        r = int(2*wp**0.4)
        if mag < 0:
            r = max(1, r + mag)
        wp += r + 20
        t = to_fixed(x, wp)
        # abs(x) > 1?
        if mag > 1:
            lg2 = ln2_fixed(wp)
            n, t = divmod(t, lg2)
        else:
            n = 0
        man = exp_series(t, wp, r)
    else:
        use_newton = False
        # put a bound on exp to avoid infinite recursion in exp_newton
        # TODO find a good bound
        if wp > LIM_EXP_SERIES2 and exp < 1000:
            if mag > 0:
                use_newton = True
            elif mag <= 0 and -mag <= ns_exp[-1]:
                i = bisect(ns_exp, -mag-1)
                if i < len(ns_exp):
                    wp0 = precs_exp[i]
                    if wp > wp0:
                        use_newton = True

        if not use_newton:
            r = int(0.7 * wp**0.5)
            if mag < 0:
                r = max(1, r + mag)
            wp += r + 20
            t = to_fixed(x, wp)
            if mag > 1:
                lg2 = ln2_fixed(wp)
                n, t = divmod(t, lg2)
            else:
                n = 0
            man = exp_series2(t, wp, r)
        else:
            # if x is very small or very large use
            # exp(x + m) = exp(x) * e**m
            if mag > LIM_MAG:
                wp += mag*10 + 100
                n = int(mag * math.log(2)) + 1
                x = mpf_sub(x, from_int(n, wp), wp)
            elif mag <= 0:
                wp += -mag*10 + 100
                if mag < 0:
                    n = int(-mag * math.log(2)) + 1
                    x = mpf_add(x, from_int(n, wp), wp)
            res = exp_newton(x, wp)
            sign, man, exp, bc = res
            if mag < 0:
                t = mpf_pow_int(mpf_e(wp), n, wp)
                res = mpf_div(res, t, wp)
                sign, man, exp, bc = res
            if mag > LIM_MAG:
                t = mpf_pow_int(mpf_e(wp), n, wp)
                res = mpf_mul(res, t, wp)
                sign, man, exp, bc = res
            return normalize(sign, man, exp, bc, prec, rnd)
    bc = bitcount(man)
    return normalize(0, man, int(-wp+n), bc, prec, rnd)
Exemple #10
0
def calc_cos_sin(which, y, swaps, prec, cos_rnd, sin_rnd):
    """
    Simultaneous computation of cos and sin (internal function).
    """
    y, wp = y
    swap_cos_sin, cos_sign, sin_sign = swaps

    if swap_cos_sin:
        which_compute = -which
    else:
        which_compute = which

    # XXX: assumes no swaps
    if not y:
        return fone, fzero

    # Tiny nonzero argument
    if wp > prec*2 + 30:
        y = from_man_exp(y, -wp)

        if swap_cos_sin:
            cos_rnd, sin_rnd = sin_rnd, cos_rnd
            cos_sign, sin_sign = sin_sign, cos_sign

        if cos_sign: cos = mpf_perturb(fnone, 0, prec, cos_rnd)
        else:        cos = mpf_perturb(fone, 1, prec, cos_rnd)
        if sin_sign: sin = mpf_perturb(mpf_neg(y), 0, prec, sin_rnd)
        else:        sin = mpf_perturb(y, 1, prec, sin_rnd)

        if swap_cos_sin:
            cos, sin = sin, cos
        return cos, sin

    # Use standard Taylor series
    if prec < 600:
        if which_compute == 0:
            sin = sin_taylor(y, wp)
            # only need to evaluate one of the series
            cos = isqrt_fast((MP_ONE<<(2*wp)) - sin*sin)
        elif which_compute == 1:
            sin = 0
            cos = cos_taylor(y, wp)
        elif which_compute == -1:
            sin = sin_taylor(y, wp)
            cos = 0
    # Use exp(i*x) with Brent's trick
    else:
        r = int(0.137 * prec**0.579)
        ep = r+20
        cos, sin = expi_series(y<<ep, wp+ep, r)
        cos >>= ep
        sin >>= ep

    if swap_cos_sin:
        cos, sin = sin, cos

    if cos_rnd is not round_nearest:
        # Round and set correct signs
        # XXX: this logic needs a second look
        ONE = MP_ONE << wp
        if cos_sign:
            cos += (-1)**(cos_rnd in (round_ceiling, round_down))
            cos = min(ONE, cos)
        else:
            cos += (-1)**(cos_rnd in (round_ceiling, round_up))
            cos = min(ONE, cos)
        if sin_sign:
            sin += (-1)**(sin_rnd in (round_ceiling, round_down))
            sin = min(ONE, sin)
        else:
            sin += (-1)**(sin_rnd in (round_ceiling, round_up))
            sin = min(ONE, sin)

    if which != -1:
        cos = normalize(cos_sign, cos, -wp, bitcount(cos), prec, cos_rnd)
    if which != 1:
        sin = normalize(sin_sign, sin, -wp, bitcount(sin), prec, sin_rnd)

    return cos, sin
Exemple #11
0
def mpf_nthroot(s, n, prec, rnd=round_fast):
    """nth-root of a positive number

    Use the Newton method when faster, otherwise use x**(1/n)
    """
    sign, man, exp, bc = s
    if sign:
        raise ComplexResult("nth root of a negative number")
    if not man:
        if s == fnan:
            return fnan
        if s == fzero:
            if n > 0:
                return fzero
            if n == 0:
                return fone
            return finf
        # Infinity
        if not n:
            return fnan
        if n < 0:
            return fzero
        return finf
    flag_inverse = False
    if n < 2:
        if n == 0:
            return fone
        if n == 1:
            return mpf_pos(s, prec, rnd)
        if n == -1:
            return mpf_div(fone, s, prec, rnd)
        # n < 0
        rnd = reciprocal_rnd[rnd]
        flag_inverse = True
        extra_inverse = 5
        prec += extra_inverse
        n = -n
    if n > 20 and (n >= 20000 or prec < int(233 + 28.3 * n**0.62)):
        prec2 = prec + 10
        fn = from_int(n)
        nth = mpf_rdiv_int(1, fn, prec2)
        r = mpf_pow(s, nth, prec2, rnd)
        s = normalize(r[0], r[1], r[2], r[3], prec, rnd)
        if flag_inverse:
            return mpf_div(fone, s, prec-extra_inverse, rnd)
        else:
            return s
    # Convert to a fixed-point number with prec2 bits.
    prec2 = prec + 2*n - (prec%n)
    # a few tests indicate that
    # for 10 < n < 10**4 a bit more precision is needed
    if n > 10:
        prec2 += prec2//10
        prec2 = prec2 - prec2%n
    # Mantissa may have more bits than we need. Trim it down.
    shift = bc - prec2
    # Adjust exponents to make prec2 and exp+shift multiples of n.
    sign1 = 0
    es = exp+shift
    if es < 0:
      sign1 = 1
      es = -es
    if sign1:
      shift += es%n
    else:
      shift -= es%n
    man = rshift(man, shift)
    extra = 10
    exp1 = ((exp+shift-(n-1)*prec2)//n) - extra
    rnd_shift = 0
    if flag_inverse:
        if rnd == 'u' or rnd == 'c':
            rnd_shift = 1
    else:
        if rnd == 'd' or rnd == 'f':
            rnd_shift = 1
    man = nthroot_fixed(man+rnd_shift, n, prec2, exp1)
    s = from_man_exp(man, exp1, prec, rnd)
    if flag_inverse:
        return mpf_div(fone, s, prec-extra_inverse, rnd)
    else:
        return s
Exemple #12
0
def mpf_nthroot(s, n, prec, rnd=round_fast):
    """nth-root of a positive number

    Use the Newton method when faster, otherwise use x**(1/n)
    """
    sign, man, exp, bc = s
    if sign:
        raise ComplexResult("nth root of a negative number")
    if not man:
        if s == fnan:
            return fnan
        if s == fzero:
            if n > 0:
                return fzero
            if n == 0:
                return fone
            return finf
        # Infinity
        if not n:
            return fnan
        if n < 0:
            return fzero
        return finf
    flag_inverse = False
    if n < 2:
        if n == 0:
            return fone
        if n == 1:
            return mpf_pos(s, prec, rnd)
        if n == -1:
            return mpf_div(fone, s, prec, rnd)
        # n < 0
        rnd = reciprocal_rnd[rnd]
        flag_inverse = True
        extra_inverse = 5
        prec += extra_inverse
        n = -n
    if n > 20 and (n >= 20000 or prec < int(233 + 28.3 * n**0.62)):
        prec2 = prec + 10
        fn = from_int(n)
        nth = mpf_rdiv_int(1, fn, prec2)
        r = mpf_pow(s, nth, prec2, rnd)
        s = normalize(r[0], r[1], r[2], r[3], prec, rnd)
        if flag_inverse:
            return mpf_div(fone, s, prec-extra_inverse, rnd)
        else:
            return s
    # Convert to a fixed-point number with prec2 bits.
    prec2 = prec + 2*n - (prec%n)
    # a few tests indicate that
    # for 10 < n < 10**4 a bit more precision is needed
    if n > 10:
        prec2 += prec2//10
        prec2 = prec2 - prec2%n
    # Mantissa may have more bits than we need. Trim it down.
    shift = bc - prec2
    # Adjust exponents to make prec2 and exp+shift multiples of n.
    sign1 = 0
    es = exp+shift
    if es < 0:
        sign1 = 1
        es = -es
    if sign1:
        shift += es%n
    else:
        shift -= es%n
    man = rshift(man, shift)
    extra = 10
    exp1 = ((exp+shift-(n-1)*prec2)//n) - extra
    rnd_shift = 0
    if flag_inverse:
        if rnd == 'u' or rnd == 'c':
            rnd_shift = 1
    else:
        if rnd == 'd' or rnd == 'f':
            rnd_shift = 1
    man = nthroot_fixed(man+rnd_shift, n, prec2, exp1)
    s = from_man_exp(man, exp1, prec, rnd)
    if flag_inverse:
        return mpf_div(fone, s, prec-extra_inverse, rnd)
    else:
        return s
Exemple #13
0
def acos_asin(z, prec, rnd, n):
    """ complex acos for n = 0, asin for n = 1
    The algorithm is described in
    T.E. Hull, T.F. Fairgrieve and P.T.P. Tang
    'Implementing the Complex Arcsine and Arcosine Functions
    using Exception Handling',
    ACM Trans. on Math. Software Vol. 23 (1997), p299
    The complex acos and asin can be defined as
    acos(z) = acos(beta) - I*sign(a)* log(alpha + sqrt(alpha**2 -1))
    asin(z) = asin(beta) + I*sign(a)* log(alpha + sqrt(alpha**2 -1))
    where z = a + I*b
    alpha = (1/2)*(r + s); beta = (1/2)*(r - s) = a/alpha
    r = sqrt((a+1)**2 + y**2); s = sqrt((a-1)**2 + y**2)
    These expressions are rewritten in different ways in different
    regions, delimited by two crossovers alpha_crossover and beta_crossover,
    and by abs(a) <= 1, in order to improve the numerical accuracy.
    """
    a, b = z
    wp = prec + 10
    # special cases with real argument
    if b == fzero:
        am = mpf_sub(fone, mpf_abs(a), wp)
        # case abs(a) <= 1
        if not am[0]:
            if n == 0:
                return mpf_acos(a, prec, rnd), fzero
            else:
                return mpf_asin(a, prec, rnd), fzero
        # cases abs(a) > 1
        else:
            # case a < -1
            if a[0]:
                pi = mpf_pi(prec, rnd)
                c = mpf_acosh(mpf_neg(a), prec, rnd)
                if n == 0:
                    return pi, mpf_neg(c)
                else:
                    return mpf_neg(mpf_shift(pi, -1)), c
            # case a > 1
            else:
                c = mpf_acosh(a, prec, rnd)
                if n == 0:
                    return fzero, c
                else:
                    pi = mpf_pi(prec, rnd)
                    return mpf_shift(pi, -1), mpf_neg(c)
    asign = bsign = 0
    if a[0]:
        a = mpf_neg(a)
        asign = 1
    if b[0]:
        b = mpf_neg(b)
        bsign = 1
    am = mpf_sub(fone, a, wp)
    ap = mpf_add(fone, a, wp)
    r = mpf_hypot(ap, b, wp)
    s = mpf_hypot(am, b, wp)
    alpha = mpf_shift(mpf_add(r, s, wp), -1)
    beta = mpf_div(a, alpha, wp)
    b2 = mpf_mul(b, b, wp)
    # case beta <= beta_crossover
    if not mpf_sub(beta_crossover, beta, wp)[0]:
        if n == 0:
            re = mpf_acos(beta, wp)
        else:
            re = mpf_asin(beta, wp)
    else:
        # to compute the real part in this region use the identity
        # asin(beta) = atan(beta/sqrt(1-beta**2))
        # beta/sqrt(1-beta**2) = (alpha + a) * (alpha - a)
        # alpha + a is numerically accurate; alpha - a can have
        # cancellations leading to numerical inaccuracies, so rewrite
        # it in differente ways according to the region
        Ax = mpf_add(alpha, a, wp)
        # case a <= 1
        if not am[0]:
            # c = b*b/(r + (a+1)); d = (s + (1-a))
            # alpha - a = (1/2)*(c + d)
            # case n=0: re = atan(sqrt((1/2) * Ax * (c + d))/a)
            # case n=1: re = atan(a/sqrt((1/2) * Ax * (c + d)))
            c = mpf_div(b2, mpf_add(r, ap, wp), wp)
            d = mpf_add(s, am, wp)
            re = mpf_shift(mpf_mul(Ax, mpf_add(c, d, wp), wp), -1)
            if n == 0:
                re = mpf_atan(mpf_div(mpf_sqrt(re, wp), a, wp), wp)
            else:
                re = mpf_atan(mpf_div(a, mpf_sqrt(re, wp), wp), wp)
        else:
            # c = Ax/(r + (a+1)); d = Ax/(s - (1-a))
            # alpha - a = (1/2)*(c + d)
            # case n = 0: re = atan(b*sqrt(c + d)/2/a)
            # case n = 1: re = atan(a/(b*sqrt(c + d)/2)
            c = mpf_div(Ax, mpf_add(r, ap, wp), wp)
            d = mpf_div(Ax, mpf_sub(s, am, wp), wp)
            re = mpf_shift(mpf_add(c, d, wp), -1)
            re = mpf_mul(b, mpf_sqrt(re, wp), wp)
            if n == 0:
                re = mpf_atan(mpf_div(re, a, wp), wp)
            else:
                re = mpf_atan(mpf_div(a, re, wp), wp)
    # to compute alpha + sqrt(alpha**2 - 1), if alpha <= alpha_crossover
    # replace it with 1 + Am1 + sqrt(Am1*(alpha+1)))
    # where Am1 = alpha -1
    # if alpha <= alpha_crossover:
    if not mpf_sub(alpha_crossover, alpha, wp)[0]:
        c1 = mpf_div(b2, mpf_add(r, ap, wp), wp)
        # case a < 1
        if mpf_neg(am)[0]:
            # Am1 = (1/2) * (b*b/(r + (a+1)) + b*b/(s + (1-a))
            c2 = mpf_add(s, am, wp)
            c2 = mpf_div(b2, c2, wp)
            Am1 = mpf_shift(mpf_add(c1, c2, wp), -1)
        else:
            # Am1 = (1/2) * (b*b/(r + (a+1)) + (s - (1-a)))
            c2 = mpf_sub(s, am, wp)
            Am1 = mpf_shift(mpf_add(c1, c2, wp), -1)
        # im = log(1 + Am1 + sqrt(Am1*(alpha+1)))
        im = mpf_mul(Am1, mpf_add(alpha, fone, wp), wp)
        im = mpf_log(mpf_add(fone, mpf_add(Am1, mpf_sqrt(im, wp), wp), wp), wp)
    else:
        # im = log(alpha + sqrt(alpha*alpha - 1))
        im = mpf_sqrt(mpf_sub(mpf_mul(alpha, alpha, wp), fone, wp), wp)
        im = mpf_log(mpf_add(alpha, im, wp), wp)
    if asign:
        if n == 0:
            re = mpf_sub(mpf_pi(wp), re, wp)
        else:
            re = mpf_neg(re)
    if not bsign and n == 0:
        im = mpf_neg(im)
    if bsign and n == 1:
        im = mpf_neg(im)
    re = normalize(re[0], re[1], re[2], re[3], prec, rnd)
    im = normalize(im[0], im[1], im[2], im[3], prec, rnd)
    return re, im
Exemple #14
0
        return (re, fzero)
    if n < 2:
        if n == 0:
            return mpc_one
        if n == 1:
            return mpc_pos((a, b), prec, rnd)
        if n == -1:
            return mpc_div(mpc_one, (a, b), prec, rnd)
        inverse = mpc_nthroot((a, b), -n, prec+5, reciprocal_rnd[rnd])
        return mpc_div(mpc_one, inverse, prec, rnd)
    if n > 20:
        fn = from_int(n)
        prec2 = prec+10
        nth = mpf_rdiv_int(1, fn, prec2)
        re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd)
        re = normalize(re[0], re[1], re[2], re[3], prec, rnd)
        im = normalize(im[0], im[1], im[2], im[3], prec, rnd)
        return re, im
    prec2 = int(1.2 * (prec + 10))
    asign, aman, aexp, abc = a
    bsign, bman, bexp, bbc = b
    af = to_fixed(a, prec2)
    bf = to_fixed(b, prec2)
    re, im = mpc_nthroot_fixed(af, bf, n, prec2)
    extra = 10
    re = from_man_exp(re, -prec2-extra, prec2, rnd)
    im = from_man_exp(im, -prec2-extra, prec2, rnd)
    return re, im

def mpc_cbrt((a, b), prec, rnd=round_fast):
    """
Exemple #15
0
def acos_asin(z, prec, rnd, n):
    """ complex acos for n = 0, asin for n = 1
    The algorithm is described in
    T.E. Hull, T.F. Fairgrieve and P.T.P. Tang
    'Implementing the Complex Arcsine and Arcosine Functions
    using Exception Handling',
    ACM Trans. on Math. Software Vol. 23 (1997), p299
    The complex acos and asin can be defined as
    acos(z) = acos(beta) - I*sign(a)* log(alpha + sqrt(alpha**2 -1))
    asin(z) = asin(beta) + I*sign(a)* log(alpha + sqrt(alpha**2 -1))
    where z = a + I*b
    alpha = (1/2)*(r + s); beta = (1/2)*(r - s) = a/alpha
    r = sqrt((a+1)**2 + y**2); s = sqrt((a-1)**2 + y**2)
    These expressions are rewritten in different ways in different
    regions, delimited by two crossovers alpha_crossover and beta_crossover,
    and by abs(a) <= 1, in order to improve the numerical accuracy.
    """
    a, b = z
    wp = prec + 10
    # special cases with real argument
    if b == fzero:
        am = mpf_sub(fone, mpf_abs(a), wp)
        # case abs(a) <= 1
        if not am[0]:
            if n == 0:
                return mpf_acos(a, prec, rnd), fzero
            else:
                return mpf_asin(a, prec, rnd), fzero
        # cases abs(a) > 1
        else:
            # case a < -1
            if a[0]:
                pi = mpf_pi(prec, rnd)
                c = mpf_acosh(mpf_neg(a), prec, rnd)
                if n == 0:
                    return pi, mpf_neg(c)
                else:
                    return mpf_neg(mpf_shift(pi, -1)), c
            # case a > 1
            else:
                c = mpf_acosh(a, prec, rnd)
                if n == 0:
                    return fzero, c
                else:
                    pi = mpf_pi(prec, rnd)
                    return mpf_shift(pi, -1), mpf_neg(c)
    asign = bsign = 0
    if a[0]:
        a = mpf_neg(a)
        asign = 1
    if b[0]:
        b = mpf_neg(b)
        bsign = 1
    am = mpf_sub(fone, a, wp)
    ap = mpf_add(fone, a, wp)
    r = mpf_hypot(ap, b, wp)
    s = mpf_hypot(am, b, wp)
    alpha = mpf_shift(mpf_add(r, s, wp), -1)
    beta = mpf_div(a, alpha, wp)
    b2 = mpf_mul(b, b, wp)
    # case beta <= beta_crossover
    if not mpf_sub(beta_crossover, beta, wp)[0]:
        if n == 0:
            re = mpf_acos(beta, wp)
        else:
            re = mpf_asin(beta, wp)
    else:
        # to compute the real part in this region use the identity
        # asin(beta) = atan(beta/sqrt(1-beta**2))
        # beta/sqrt(1-beta**2) = (alpha + a) * (alpha - a)
        # alpha + a is numerically accurate; alpha - a can have
        # cancellations leading to numerical inaccuracies, so rewrite
        # it in differente ways according to the region
        Ax = mpf_add(alpha, a, wp)
        # case a <= 1
        if not am[0]:
            # c = b*b/(r + (a+1)); d = (s + (1-a))
            # alpha - a = (1/2)*(c + d)
            # case n=0: re = atan(sqrt((1/2) * Ax * (c + d))/a)
            # case n=1: re = atan(a/sqrt((1/2) * Ax * (c + d)))
            c = mpf_div(b2, mpf_add(r, ap, wp), wp)
            d = mpf_add(s, am, wp)
            re = mpf_shift(mpf_mul(Ax, mpf_add(c, d, wp), wp), -1)
            if n == 0:
                re = mpf_atan(mpf_div(mpf_sqrt(re, wp), a, wp), wp)
            else:
                re = mpf_atan(mpf_div(a, mpf_sqrt(re, wp), wp), wp)
        else:
            # c = Ax/(r + (a+1)); d = Ax/(s - (1-a))
            # alpha - a = (1/2)*(c + d)
            # case n = 0: re = atan(b*sqrt(c + d)/2/a)
            # case n = 1: re = atan(a/(b*sqrt(c + d)/2)
            c = mpf_div(Ax, mpf_add(r, ap, wp), wp)
            d = mpf_div(Ax, mpf_sub(s, am, wp), wp)
            re = mpf_shift(mpf_add(c, d, wp), -1)
            re = mpf_mul(b, mpf_sqrt(re, wp), wp)
            if n == 0:
                re = mpf_atan(mpf_div(re, a, wp), wp)
            else:
                re = mpf_atan(mpf_div(a, re, wp), wp)
    # to compute alpha + sqrt(alpha**2 - 1), if alpha <= alpha_crossover
    # replace it with 1 + Am1 + sqrt(Am1*(alpha+1)))
    # where Am1 = alpha -1
    # if alpha <= alpha_crossover:
    if not mpf_sub(alpha_crossover, alpha, wp)[0]:
        c1 = mpf_div(b2, mpf_add(r, ap, wp), wp)
        # case a < 1
        if mpf_neg(am)[0]:
            # Am1 = (1/2) * (b*b/(r + (a+1)) + b*b/(s + (1-a))
            c2 = mpf_add(s, am, wp)
            c2 = mpf_div(b2, c2, wp)
            Am1 = mpf_shift(mpf_add(c1, c2, wp), -1)
        else:
            # Am1 = (1/2) * (b*b/(r + (a+1)) + (s - (1-a)))
            c2 = mpf_sub(s, am, wp)
            Am1 = mpf_shift(mpf_add(c1, c2, wp), -1)
        # im = log(1 + Am1 + sqrt(Am1*(alpha+1)))
        im = mpf_mul(Am1, mpf_add(alpha, fone, wp), wp)
        im = mpf_log(mpf_add(fone, mpf_add(Am1, mpf_sqrt(im, wp), wp), wp), wp)
    else:
        # im = log(alpha + sqrt(alpha*alpha - 1))
        im = mpf_sqrt(mpf_sub(mpf_mul(alpha, alpha, wp), fone, wp), wp)
        im = mpf_log(mpf_add(alpha, im, wp), wp)
    if asign:
        if n == 0:
            re = mpf_sub(mpf_pi(wp), re, wp)
        else:
            re = mpf_neg(re)
    if not bsign and n == 0:
        im = mpf_neg(im)
    if bsign and n == 1:
        im = mpf_neg(im)
    re = normalize(re[0], re[1], re[2], re[3], prec, rnd)
    im = normalize(im[0], im[1], im[2], im[3], prec, rnd)
    return re, im
Exemple #16
0
def mpf_log(x, prec, rnd=round_fast):
    """
    Compute the natural logarithm of the mpf value x. If x is negative,
    ComplexResult is raised.
    """
    sign, man, exp, bc = x
    #------------------------------------------------------------------
    # Handle special values
    if not man:
        if x == fzero: return fninf
        if x == finf: return finf
        if x == fnan: return fnan
    if sign:
        raise ComplexResult("logarithm of a negative number")
    wp = prec + 20
    #------------------------------------------------------------------
    # Handle log(2^n) = log(n)*2.
    # Here we catch the only possible exact value, log(1) = 0
    if man == 1:
        if not exp:
            return fzero
        return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd)
    mag = exp+bc
    abs_mag = abs(mag)
    #------------------------------------------------------------------
    # Handle x = 1+eps, where log(x) ~ x. We need to check for
    # cancellation when moving to fixed-point math and compensate
    # by increasing the precision. Note that abs_mag in (0, 1) <=>
    # 0.5 < x < 2 and x != 1
    if abs_mag <= 1:
        # Calculate t = x-1 to measure distance from 1 in bits
        tsign = 1-abs_mag
        if tsign:
            tman = (MP_ONE<<bc) - man
        else:
            tman = man - (MP_ONE<<(bc-1))
        tbc = bitcount(tman)
        cancellation = bc - tbc
        if cancellation > wp:
            t = normalize(tsign, tman, abs_mag-bc, tbc, tbc, 'n')
            return mpf_perturb(t, tsign, prec, rnd)
        else:
            wp += cancellation
        # TODO: if close enough to 1, we could use Taylor series
        # even in the AGM precision range, since the Taylor series
        # converges rapidly
    #------------------------------------------------------------------
    # Another special case:
    # n*log(2) is a good enough approximation
    if abs_mag > 10000:
        if bitcount(abs_mag) > wp:
            return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd)
    #------------------------------------------------------------------
    # General case.
    # Perform argument reduction using log(x) = log(x*2^n) - n*log(2):
    # If we are in the Taylor precision range, choose magnitude 0 or 1.
    # If we are in the AGM precision range, choose magnitude -m for
    # some large m; benchmarking on one machine showed m = prec/20 to be
    # optimal between 1000 and 100,000 digits.
    if wp <= LOG_TAYLOR_PREC:
        m = log_taylor_cached(lshift(man, wp-bc), wp)
        if mag:
            m += mag*ln2_fixed(wp)
    else:
        optimal_mag = -wp//LOG_AGM_MAG_PREC_RATIO
        n = optimal_mag - mag
        x = mpf_shift(x, n)
        wp += (-optimal_mag)
        m = -log_agm(to_fixed(x, wp), wp)
        m -= n*ln2_fixed(wp)
    return from_man_exp(m, -wp, prec, rnd)
Exemple #17
0
 def f(prec, rnd=round_fast):
     wp = prec + 20
     v = fixed(wp)
     if rnd in (round_up, round_ceiling):
         v += 1
     return normalize(0, v, -wp, bitcount(v), prec, rnd)
Exemple #18
0
 def f(prec, rnd=round_fast):
     wp = prec + 20
     v = fixed(wp)
     if rnd in (round_up, round_ceiling):
         v += 1
     return normalize(0, v, -wp, bitcount(v), prec, rnd)
Exemple #19
0
def calc_cos_sin(which, y, swaps, prec, cos_rnd, sin_rnd):
    """
    Simultaneous computation of cos and sin (internal function).
    """
    y, wp = y
    swap_cos_sin, cos_sign, sin_sign = swaps

    if swap_cos_sin:
        which_compute = -which
    else:
        which_compute = which

    # XXX: assumes no swaps
    if not y:
        return fone, fzero

    # Tiny nonzero argument
    if wp > prec*2 + 30:
        y = from_man_exp(y, -wp)

        if swap_cos_sin:
            cos_rnd, sin_rnd = sin_rnd, cos_rnd
            cos_sign, sin_sign = sin_sign, cos_sign

        if cos_sign: cos = mpf_perturb(fnone, 0, prec, cos_rnd)
        else:        cos = mpf_perturb(fone, 1, prec, cos_rnd)
        if sin_sign: sin = mpf_perturb(mpf_neg(y), 0, prec, sin_rnd)
        else:        sin = mpf_perturb(y, 1, prec, sin_rnd)

        if swap_cos_sin:
            cos, sin = sin, cos
        return cos, sin

    # Use standard Taylor series
    if prec < 600:
        if which_compute == 0:
            sin = sin_taylor(y, wp)
            # only need to evaluate one of the series
            cos = sqrt_fixed((1<<wp) - ((sin*sin)>>wp), wp)
        elif which_compute == 1:
            sin = 0
            cos = cos_taylor(y, wp)
        elif which_compute == -1:
            sin = sin_taylor(y, wp)
            cos = 0
    # Use exp(i*x) with Brent's trick
    else:
        r = int(0.137 * prec**0.579)
        ep = r+20
        cos, sin = expi_series(y<<ep, wp+ep, r)
        cos >>= ep
        sin >>= ep

    if swap_cos_sin:
        cos, sin = sin, cos

    if cos_rnd is not round_nearest:
        # Round and set correct signs
        # XXX: this logic needs a second look
        ONE = MP_ONE << wp
        if cos_sign:
            cos += (-1)**(cos_rnd in (round_ceiling, round_down))
            cos = min(ONE, cos)
        else:
            cos += (-1)**(cos_rnd in (round_ceiling, round_up))
            cos = min(ONE, cos)
        if sin_sign:
            sin += (-1)**(sin_rnd in (round_ceiling, round_down))
            sin = min(ONE, sin)
        else:
            sin += (-1)**(sin_rnd in (round_ceiling, round_up))
            sin = min(ONE, sin)

    if which != -1:
        cos = normalize(cos_sign, cos, -wp, bitcount(cos), prec, cos_rnd)
    if which != 1:
        sin = normalize(sin_sign, sin, -wp, bitcount(sin), prec, sin_rnd)

    return cos, sin
Exemple #20
0
        return (re, fzero)
    if n < 2:
        if n == 0:
            return mpc_one
        if n == 1:
            return mpc_pos((a, b), prec, rnd)
        if n == -1:
            return mpc_div(mpc_one, (a, b), prec, rnd)
        inverse = mpc_nthroot((a, b), -n, prec + 5, reciprocal_rnd[rnd])
        return mpc_div(mpc_one, inverse, prec, rnd)
    if n > 20:
        fn = from_int(n)
        prec2 = prec + 10
        nth = mpf_rdiv_int(1, fn, prec2)
        re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd)
        re = normalize(re[0], re[1], re[2], re[3], prec, rnd)
        im = normalize(im[0], im[1], im[2], im[3], prec, rnd)
        return re, im
    prec2 = int(1.2 * (prec + 10))
    asign, aman, aexp, abc = a
    bsign, bman, bexp, bbc = b
    af = to_fixed(a, prec2)
    bf = to_fixed(b, prec2)
    re, im = mpc_nthroot_fixed(af, bf, n, prec2)
    extra = 10
    re = from_man_exp(re, -prec2 - extra, prec2, rnd)
    im = from_man_exp(im, -prec2 - extra, prec2, rnd)
    return re, im


def mpc_cbrt((a, b), prec, rnd=round_fast):