/* try asymptotic expansion when x is large and negative: Li2(x) = -log(-x)^2/2 - Pi^2/6 - 1/x + O(1/x^2). More precisely for x <= -2 we have for g(x) = -log(-x)^2/2 - Pi^2/6: |Li2(x) - g(x)| <= 1/|x|. Assumes x <= -7, which ensures |log(-x)^2/2| >= Pi^2/6, and g(x) <= -3.5. Return 0 if asymptotic expansion failed (unable to round), otherwise returns correct ternary value. */ static int mpfr_li2_asympt_neg (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode) { mpfr_t g, h; mp_prec_t w = MPFR_PREC (y) + 20; int inex = 0; MPFR_ASSERTN (mpfr_cmp_si (x, -7) <= 0); mpfr_init2 (g, w); mpfr_init2 (h, w); mpfr_neg (g, x, GMP_RNDN); mpfr_log (g, g, GMP_RNDN); /* rel. error <= |(1 + theta) - 1| */ mpfr_sqr (g, g, GMP_RNDN); /* rel. error <= |(1 + theta)^3 - 1| <= 2^(2-w) */ mpfr_div_2ui (g, g, 1, GMP_RNDN); /* rel. error <= 2^(2-w) */ mpfr_const_pi (h, GMP_RNDN); /* error <= 2^(1-w) */ mpfr_sqr (h, h, GMP_RNDN); /* rel. error <= 2^(2-w) */ mpfr_div_ui (h, h, 6, GMP_RNDN); /* rel. error <= |(1 + theta)^4 - 1| <= 5 * 2^(-w) */ MPFR_ASSERTN (MPFR_EXP (g) >= MPFR_EXP (h)); mpfr_add (g, g, h, GMP_RNDN); /* err <= ulp(g)/2 + g*2^(2-w) + g*5*2^(-w) <= ulp(g) * (1/2 + 4 + 5) < 10 ulp(g). If in addition |1/x| <= 4 ulp(g), then the total error is bounded by 16 ulp(g). */ if ((MPFR_EXP (x) >= (mp_exp_t) (w - 2) - MPFR_EXP (g)) && MPFR_CAN_ROUND (g, w - 4, MPFR_PREC (y), rnd_mode)) inex = mpfr_neg (y, g, rnd_mode); mpfr_clear (g); mpfr_clear (h); return inex; }
static void test_grandom (long nbtests, mpfr_prec_t prec, mpfr_rnd_t rnd, int verbose) { mpfr_t *t; mpfr_t av, va, tmp; int i, inexact; nbtests = (nbtests & 1) ? (nbtests + 1) : nbtests; t = (mpfr_t *) tests_allocate (nbtests * sizeof (mpfr_t)); for (i = 0; i < nbtests; ++i) mpfr_init2 (t[i], prec); for (i = 0; i < nbtests; i += 2) { inexact = mpfr_grandom (t[i], t[i + 1], RANDS, MPFR_RNDN); if ((inexact & 3) == 0 || (inexact & (3 << 2)) == 0) { /* one call in the loop pretended to return an exact number! */ printf ("Error: mpfr_grandom() returns a zero ternary value.\n"); exit (1); } } #ifdef HAVE_STDARG if (verbose) { mpfr_init2 (av, prec); mpfr_init2 (va, prec); mpfr_init2 (tmp, prec); mpfr_set_ui (av, 0, MPFR_RNDN); mpfr_set_ui (va, 0, MPFR_RNDN); for (i = 0; i < nbtests; ++i) { mpfr_add (av, av, t[i], MPFR_RNDN); mpfr_sqr (tmp, t[i], MPFR_RNDN); mpfr_add (va, va, tmp, MPFR_RNDN); } mpfr_div_ui (av, av, nbtests, MPFR_RNDN); mpfr_div_ui (va, va, nbtests, MPFR_RNDN); mpfr_sqr (tmp, av, MPFR_RNDN); mpfr_sub (va, va, av, MPFR_RNDN); mpfr_printf ("Average = %.5Rf\nVariance = %.5Rf\n", av, va); mpfr_clear (av); mpfr_clear (va); mpfr_clear (tmp); } #endif /* HAVE_STDARG */ for (i = 0; i < nbtests; ++i) mpfr_clear (t[i]); tests_free (t, nbtests * sizeof (mpfr_t)); return; }
/* the rounding mode is mpfr_rnd_t here since we return an mpfr number */ int mpc_norm (mpfr_ptr a, mpc_srcptr b, mpfr_rnd_t rnd) { mpfr_t u, v; mp_prec_t prec; int inexact, overflow; prec = MPFR_PREC(a); /* handling of special values; consistent with abs in that norm = abs^2; so norm (+-inf, nan) = norm (nan, +-inf) = +inf */ if ( (mpfr_nan_p (MPC_RE (b)) || mpfr_nan_p (MPC_IM (b))) || (mpfr_inf_p (MPC_RE (b)) || mpfr_inf_p (MPC_IM (b)))) return mpc_abs (a, b, rnd); mpfr_init (u); mpfr_init (v); if (!mpfr_zero_p(MPC_RE(b)) && !mpfr_zero_p(MPC_IM(b)) && 2 * SAFE_ABS (mp_exp_t, MPFR_EXP (MPC_RE (b)) - MPFR_EXP (MPC_IM (b))) > (mp_exp_t)prec) /* If real and imaginary part have very different magnitudes, then the */ /* generic code increases the precision too much. Instead, compute the */ /* squarings _exactly_. */ { mpfr_set_prec (u, 2 * MPFR_PREC (MPC_RE (b))); mpfr_set_prec (v, 2 * MPFR_PREC (MPC_IM (b))); mpfr_sqr (u, MPC_RE (b), GMP_RNDN); mpfr_sqr (v, MPC_IM (b), GMP_RNDN); inexact = mpfr_add (a, u, v, rnd); } else { do { prec += mpc_ceil_log2 (prec) + 3; mpfr_set_prec (u, prec); mpfr_set_prec (v, prec); inexact = mpfr_sqr (u, MPC_RE(b), GMP_RNDN); /* err<=1/2ulp */ inexact |= mpfr_sqr (v, MPC_IM(b), GMP_RNDN); /* err<=1/2ulp*/ inexact |= mpfr_add (u, u, v, GMP_RNDN); /* err <= 3/2 ulps */ overflow = mpfr_inf_p (u); } while (!overflow && inexact && mpfr_can_round (u, prec - 2, GMP_RNDN, rnd, MPFR_PREC(a)) == 0); inexact |= mpfr_set (a, u, rnd); } mpfr_clear (u); mpfr_clear (v); return inexact; }
void Compute(gmp_randstate_t r) const { // The algorithm is sample x and z from the exponential distribution; if // (x-1)^2 < 2*z, return (random sign)*x; otherwise repeat. Probability // of acceptance is sqrt(pi/2) * exp(-1/2) = 0.7602. while (true) { _edist(_x, r); _edist(_z, r); for (mp_size_t k = 1; ; ++k) { _x.ExpandTo(r, k - 1); _z.ExpandTo(r, k - 1); mpfr_prec_t prec = std::max(mpfr_prec_t(MPFR_PREC_MIN), k * bits); mpfr_set_prec(_xf, prec); mpfr_set_prec(_zf, prec); // Try for acceptance first; so compute upper limit on (y-1)^2 and // lower limit on 2*z. if (_x.UInteger() == 0) { _x(_xf, MPFR_RNDD); mpfr_ui_sub(_xf, 1u, _xf, MPFR_RNDU); } else { _x(_xf, MPFR_RNDU); mpfr_sub_ui(_xf, _xf, 1u, MPFR_RNDU); } mpfr_sqr(_xf, _xf, MPFR_RNDU); _z(_zf, MPFR_RNDD); mpfr_mul_2ui(_zf, _zf, 1u, MPFR_RNDD); if (mpfr_cmp(_xf, _zf) < 0) { // (y-1)^2 < 2*z, so accept if (_x.Boolean(r)) _x.Negate(); // include a random sign return; } // Try for rejection; so compute lower limit on (y-1)^2 and upper // limit on 2*z. if (_x.UInteger() == 0) { _x(_xf, MPFR_RNDU); mpfr_ui_sub(_xf, 1u, _xf, MPFR_RNDD); } else { _x(_xf, MPFR_RNDD); mpfr_sub_ui(_xf, _xf, 1u, MPFR_RNDD); } mpfr_sqr(_xf, _xf, MPFR_RNDD); _z(_zf, MPFR_RNDU); mpfr_mul_2ui(_zf, _zf, 1u, MPFR_RNDU); if (mpfr_cmp(_xf, _zf) > 0) // (y-1)^2 > 2*z, so reject break; // Otherwise repeat with more precision } // Reject and start over with a new y and z } }
SeedValue seed_mpfr_sqr (SeedContext ctx, SeedObject function, SeedObject this_object, gsize argument_count, const SeedValue args[], SeedException *exception) { mpfr_rnd_t rnd; mpfr_ptr rop, op; gint ret; CHECK_ARG_COUNT("mpfr.sqr", 2); rop = seed_object_get_private(this_object); rnd = seed_value_to_mpfr_rnd_t(ctx, args[1], exception); if ( seed_value_is_object_of_class(ctx, args[0], mpfr_class) ) { op = seed_object_get_private(args[0]); } else { TYPE_EXCEPTION("mpfr.sqr", "mpfr_t"); } ret = mpfr_sqr(rop, op, rnd); return seed_value_from_int(ctx, ret, exception); }
/* try asymptotic expansion when x is large and positive: Li2(x) = -log(x)^2/2 + Pi^2/3 - 1/x + O(1/x^2). More precisely for x >= 2 we have for g(x) = -log(x)^2/2 + Pi^2/3: -2 <= x * (Li2(x) - g(x)) <= -1 thus |Li2(x) - g(x)| <= 2/x. Assumes x >= 38, which ensures log(x)^2/2 >= 2*Pi^2/3, and g(x) <= -3.3. Return 0 if asymptotic expansion failed (unable to round), otherwise returns correct ternary value. */ static int mpfr_li2_asympt_pos (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode) { mpfr_t g, h; mp_prec_t w = MPFR_PREC (y) + 20; int inex = 0; MPFR_ASSERTN (mpfr_cmp_ui (x, 38) >= 0); mpfr_init2 (g, w); mpfr_init2 (h, w); mpfr_log (g, x, GMP_RNDN); /* rel. error <= |(1 + theta) - 1| */ mpfr_sqr (g, g, GMP_RNDN); /* rel. error <= |(1 + theta)^3 - 1| <= 2^(2-w) */ mpfr_div_2ui (g, g, 1, GMP_RNDN); /* rel. error <= 2^(2-w) */ mpfr_const_pi (h, GMP_RNDN); /* error <= 2^(1-w) */ mpfr_sqr (h, h, GMP_RNDN); /* rel. error <= 2^(2-w) */ mpfr_div_ui (h, h, 3, GMP_RNDN); /* rel. error <= |(1 + theta)^4 - 1| <= 5 * 2^(-w) */ /* since x is chosen such that log(x)^2/2 >= 2 * (Pi^2/3), we should have g >= 2*h, thus |g-h| >= |h|, and the relative error on g is at most multiplied by 2 in the difference, and that by h is unchanged. */ MPFR_ASSERTN (MPFR_EXP (g) > MPFR_EXP (h)); mpfr_sub (g, h, g, GMP_RNDN); /* err <= ulp(g)/2 + g*2^(3-w) + g*5*2^(-w) <= ulp(g) * (1/2 + 8 + 5) < 14 ulp(g). If in addition 2/x <= 2 ulp(g), i.e., 1/x <= ulp(g), then the total error is bounded by 16 ulp(g). */ if ((MPFR_EXP (x) >= (mp_exp_t) w - MPFR_EXP (g)) && MPFR_CAN_ROUND (g, w - 4, MPFR_PREC (y), rnd_mode)) inex = mpfr_set (y, g, rnd_mode); mpfr_clear (g); mpfr_clear (h); return inex; }
static void check_special (void) { mpfr_t x, y; mpfr_exp_t emin; mpfr_init (x); mpfr_init (y); mpfr_set_nan (x); mpfr_sqr (y, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_nan_p (y)); mpfr_set_inf (x, 1); mpfr_sqr (y, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_inf_p (y) && mpfr_sgn (y) > 0); mpfr_set_inf (x, -1); mpfr_sqr (y, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_inf_p (y) && mpfr_sgn (y) > 0); mpfr_set_ui (x, 0, MPFR_RNDN); mpfr_sqr (y, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_zero_p (y)); emin = mpfr_get_emin (); mpfr_set_emin (0); mpfr_set_ui (x, 1, MPFR_RNDN); mpfr_div_2ui (x, x, 1, MPFR_RNDN); MPFR_ASSERTN (!mpfr_zero_p (x)); mpfr_sqr (y, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_zero_p (y)); mpfr_set_emin (emin); mpfr_clear (y); mpfr_clear (x); }
/* return add = 1 + floor(log(c^3*(13+m1))/log(2)) where c = (1+eps)*(1+eps*max(8,m1)), m1 = 1 + max(1/eps,2*sd)*(1+eps), eps = 2^(-precz-14) sd = abs(s-1) */ static long compute_add (mpfr_srcptr s, mpfr_prec_t precz) { mpfr_t t, u, m1; long add; mpfr_inits2 (64, t, u, m1, (mpfr_ptr) 0); if (mpfr_cmp_ui (s, 1) >= 0) mpfr_sub_ui (t, s, 1, MPFR_RNDU); else mpfr_ui_sub (t, 1, s, MPFR_RNDU); /* now t = sd = abs(s-1), rounded up */ mpfr_set_ui_2exp (u, 1, - precz - 14, MPFR_RNDU); /* u = eps */ /* since 1/eps = 2^(precz+14), if EXP(sd) >= precz+14, then sd >= 1/2*2^(precz+14) thus 2*sd >= 2^(precz+14) >= 1/eps */ if (mpfr_get_exp (t) >= precz + 14) mpfr_mul_2exp (t, t, 1, MPFR_RNDU); else mpfr_set_ui_2exp (t, 1, precz + 14, MPFR_RNDU); /* now t = max(1/eps,2*sd) */ mpfr_add_ui (u, u, 1, MPFR_RNDU); /* u = 1+eps, rounded up */ mpfr_mul (t, t, u, MPFR_RNDU); /* t = max(1/eps,2*sd)*(1+eps) */ mpfr_add_ui (m1, t, 1, MPFR_RNDU); if (mpfr_get_exp (m1) <= 3) mpfr_set_ui (t, 8, MPFR_RNDU); else mpfr_set (t, m1, MPFR_RNDU); /* now t = max(8,m1) */ mpfr_div_2exp (t, t, precz + 14, MPFR_RNDU); /* eps*max(8,m1) */ mpfr_add_ui (t, t, 1, MPFR_RNDU); /* 1+eps*max(8,m1) */ mpfr_mul (t, t, u, MPFR_RNDU); /* t = c */ mpfr_add_ui (u, m1, 13, MPFR_RNDU); /* 13+m1 */ mpfr_mul (u, u, t, MPFR_RNDU); /* c*(13+m1) */ mpfr_sqr (t, t, MPFR_RNDU); /* c^2 */ mpfr_mul (u, u, t, MPFR_RNDU); /* c^3*(13+m1) */ add = mpfr_get_exp (u); mpfr_clears (t, u, m1, (mpfr_ptr) 0); return add; }
static void check_random (mpfr_prec_t p) { mpfr_t x,y,z; int r; int i, inexact1, inexact2; mpfr_inits2 (p, x, y, z, (mpfr_ptr) 0); for(i = 0 ; i < 500 ; i++) { mpfr_urandomb (x, RANDS); if (MPFR_IS_PURE_FP(x)) for (r = 0 ; r < MPFR_RND_MAX ; r++) { inexact1 = mpfr_mul (y, x, x, (mpfr_rnd_t) r); inexact2 = mpfr_sqr (z, x, (mpfr_rnd_t) r); if (mpfr_cmp (y, z)) error1 ((mpfr_rnd_t) r,p,x,y,z); if (inexact_sign (inexact1) != inexact_sign (inexact2)) error2 ((mpfr_rnd_t) r,p,x,y,inexact1,inexact2); } } mpfr_clears (x, y, z, (mpfr_ptr) 0); }
/* (y, z) <- (sin(x), cos(x)), return value is 0 iff both results are exact ie, iff x = 0 */ int mpfr_sin_cos (mpfr_ptr y, mpfr_ptr z, mpfr_srcptr x, mp_rnd_t rnd_mode) { mp_prec_t prec, m; int neg, reduce; mpfr_t c, xr; mpfr_srcptr xx; mp_exp_t err, expx; MPFR_ZIV_DECL (loop); if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x))) { if (MPFR_IS_NAN(x) || MPFR_IS_INF(x)) { MPFR_SET_NAN (y); MPFR_SET_NAN (z); MPFR_RET_NAN; } else /* x is zero */ { MPFR_ASSERTD (MPFR_IS_ZERO (x)); MPFR_SET_ZERO (y); MPFR_SET_SAME_SIGN (y, x); /* y = 0, thus exact, but z is inexact in case of underflow or overflow */ return mpfr_set_ui (z, 1, rnd_mode); } } MPFR_LOG_FUNC (("x[%#R]=%R rnd=%d", x, x, rnd_mode), ("sin[%#R]=%R cos[%#R]=%R", y, y, z, z)); prec = MAX (MPFR_PREC (y), MPFR_PREC (z)); m = prec + MPFR_INT_CEIL_LOG2 (prec) + 13; expx = MPFR_GET_EXP (x); mpfr_init (c); mpfr_init (xr); MPFR_ZIV_INIT (loop, m); for (;;) { /* the following is copied from sin.c */ if (expx >= 2) /* reduce the argument */ { reduce = 1; mpfr_set_prec (c, expx + m - 1); mpfr_set_prec (xr, m); mpfr_const_pi (c, GMP_RNDN); mpfr_mul_2ui (c, c, 1, GMP_RNDN); mpfr_remainder (xr, x, c, GMP_RNDN); mpfr_div_2ui (c, c, 1, GMP_RNDN); if (MPFR_SIGN (xr) > 0) mpfr_sub (c, c, xr, GMP_RNDZ); else mpfr_add (c, c, xr, GMP_RNDZ); if (MPFR_IS_ZERO(xr) || MPFR_EXP(xr) < (mp_exp_t) 3 - (mp_exp_t) m || MPFR_EXP(c) < (mp_exp_t) 3 - (mp_exp_t) m) goto next_step; xx = xr; } else /* the input argument is already reduced */ { reduce = 0; xx = x; } neg = MPFR_IS_NEG (xx); /* gives sign of sin(x) */ mpfr_set_prec (c, m); mpfr_cos (c, xx, GMP_RNDZ); /* If no argument reduction was performed, the error is at most ulp(c), otherwise it is at most ulp(c) + 2^(2-m). Since |c| < 1, we have ulp(c) <= 2^(-m), thus the error is bounded by 2^(3-m) in that later case. */ if (reduce == 0) err = m; else err = MPFR_GET_EXP (c) + (mp_exp_t) (m - 3); if (!mpfr_can_round (c, err, GMP_RNDN, rnd_mode, MPFR_PREC (z) + (rnd_mode == GMP_RNDN))) goto next_step; mpfr_set (z, c, rnd_mode); mpfr_sqr (c, c, GMP_RNDU); mpfr_ui_sub (c, 1, c, GMP_RNDN); err = 2 + (- MPFR_GET_EXP (c)) / 2; mpfr_sqrt (c, c, GMP_RNDN); if (neg) MPFR_CHANGE_SIGN (c); /* the absolute error on c is at most 2^(err-m), which we must put in the form 2^(EXP(c)-err). If there was an argument reduction, we need to add 2^(2-m); since err >= 2, the error is bounded by 2^(err+1-m) in that case. */ err = MPFR_GET_EXP (c) + (mp_exp_t) m - (err + reduce); if (mpfr_can_round (c, err, GMP_RNDN, rnd_mode, MPFR_PREC (y) + (rnd_mode == GMP_RNDN))) break; /* check for huge cancellation */ if (err < (mp_exp_t) MPFR_PREC (y)) m += MPFR_PREC (y) - err; /* Check if near 1 */ if (MPFR_GET_EXP (c) == 1 && MPFR_MANT (c)[MPFR_LIMB_SIZE (c)-1] == MPFR_LIMB_HIGHBIT) m += m; next_step: MPFR_ZIV_NEXT (loop, m); mpfr_set_prec (c, m); } MPFR_ZIV_FREE (loop); mpfr_set (y, c, rnd_mode); mpfr_clear (c); mpfr_clear (xr); MPFR_RET (1); /* Always inexact */ }
/* agm(x,y) is between x and y, so we don't need to save exponent range */ int mpfr_agm (mpfr_ptr r, mpfr_srcptr op2, mpfr_srcptr op1, mp_rnd_t rnd_mode) { int compare, inexact; mp_size_t s; mp_prec_t p, q; mp_limb_t *up, *vp, *tmpp; mpfr_t u, v, tmp; unsigned long n; /* number of iterations */ unsigned long err = 0; MPFR_ZIV_DECL (loop); MPFR_TMP_DECL(marker); MPFR_LOG_FUNC (("op2[%#R]=%R op1[%#R]=%R rnd=%d", op2,op2,op1,op1,rnd_mode), ("r[%#R]=%R inexact=%d", r, r, inexact)); /* Deal with special values */ if (MPFR_ARE_SINGULAR (op1, op2)) { /* If a or b is NaN, the result is NaN */ if (MPFR_IS_NAN(op1) || MPFR_IS_NAN(op2)) { MPFR_SET_NAN(r); MPFR_RET_NAN; } /* now one of a or b is Inf or 0 */ /* If a and b is +Inf, the result is +Inf. Otherwise if a or b is -Inf or 0, the result is NaN */ else if (MPFR_IS_INF(op1) || MPFR_IS_INF(op2)) { if (MPFR_IS_STRICTPOS(op1) && MPFR_IS_STRICTPOS(op2)) { MPFR_SET_INF(r); MPFR_SET_SAME_SIGN(r, op1); MPFR_RET(0); /* exact */ } else { MPFR_SET_NAN(r); MPFR_RET_NAN; } } else /* a and b are neither NaN nor Inf, and one is zero */ { /* If a or b is 0, the result is +0 since a sqrt is positive */ MPFR_ASSERTD (MPFR_IS_ZERO (op1) || MPFR_IS_ZERO (op2)); MPFR_SET_POS (r); MPFR_SET_ZERO (r); MPFR_RET (0); /* exact */ } } MPFR_CLEAR_FLAGS (r); /* If a or b is negative (excluding -Infinity), the result is NaN */ if (MPFR_UNLIKELY(MPFR_IS_NEG(op1) || MPFR_IS_NEG(op2))) { MPFR_SET_NAN(r); MPFR_RET_NAN; } /* Precision of the following calculus */ q = MPFR_PREC(r); p = q + MPFR_INT_CEIL_LOG2(q) + 15; MPFR_ASSERTD (p >= 7); /* see algorithms.tex */ s = (p - 1) / BITS_PER_MP_LIMB + 1; /* b (op2) and a (op1) are the 2 operands but we want b >= a */ compare = mpfr_cmp (op1, op2); if (MPFR_UNLIKELY( compare == 0 )) { mpfr_set (r, op1, rnd_mode); MPFR_RET (0); /* exact */ } else if (compare > 0) { mpfr_srcptr t = op1; op1 = op2; op2 = t; } /* Now b(=op2) >= a (=op1) */ MPFR_TMP_MARK(marker); /* Main loop */ MPFR_ZIV_INIT (loop, p); for (;;) { mp_prec_t eq; /* Init temporary vars */ MPFR_TMP_INIT (up, u, p, s); MPFR_TMP_INIT (vp, v, p, s); MPFR_TMP_INIT (tmpp, tmp, p, s); /* Calculus of un and vn */ mpfr_mul (u, op1, op2, GMP_RNDN); /* Faster since PREC(op) < PREC(u) */ mpfr_sqrt (u, u, GMP_RNDN); mpfr_add (v, op1, op2, GMP_RNDN); /* add with !=prec is still good*/ mpfr_div_2ui (v, v, 1, GMP_RNDN); n = 1; while (mpfr_cmp2 (u, v, &eq) != 0 && eq <= p - 2) { mpfr_add (tmp, u, v, GMP_RNDN); mpfr_div_2ui (tmp, tmp, 1, GMP_RNDN); /* See proof in algorithms.tex */ if (4*eq > p) { mpfr_t w; /* tmp = U(k) */ mpfr_init2 (w, (p + 1) / 2); mpfr_sub (w, v, u, GMP_RNDN); /* e = V(k-1)-U(k-1) */ mpfr_sqr (w, w, GMP_RNDN); /* e = e^2 */ mpfr_div_2ui (w, w, 4, GMP_RNDN); /* e*= (1/2)^2*1/4 */ mpfr_div (w, w, tmp, GMP_RNDN); /* 1/4*e^2/U(k) */ mpfr_sub (v, tmp, w, GMP_RNDN); err = MPFR_GET_EXP (tmp) - MPFR_GET_EXP (v); /* 0 or 1 */ mpfr_clear (w); break; } mpfr_mul (u, u, v, GMP_RNDN); mpfr_sqrt (u, u, GMP_RNDN); mpfr_swap (v, tmp); n ++; } /* the error on v is bounded by (18n+51) ulps, or twice if there was an exponent loss in the final subtraction */ err += MPFR_INT_CEIL_LOG2(18 * n + 51); /* 18n+51 should not overflow since n is about log(p) */ /* we should have n+2 <= 2^(p/4) [see algorithms.tex] */ if (MPFR_LIKELY (MPFR_INT_CEIL_LOG2(n + 2) <= p / 4 && MPFR_CAN_ROUND (v, p - err, q, rnd_mode))) break; /* Stop the loop */ /* Next iteration */ MPFR_ZIV_NEXT (loop, p); s = (p - 1) / BITS_PER_MP_LIMB + 1; } MPFR_ZIV_FREE (loop); /* Setting of the result */ inexact = mpfr_set (r, v, rnd_mode); /* Let's clean */ MPFR_TMP_FREE(marker); return inexact; /* agm(u,v) can be exact for u, v rational only for u=v. Proof (due to Nicolas Brisebarre): it suffices to consider u=1 and v<1. Then 1/AGM(1,v) = 2F1(1/2,1/2,1;1-v^2), and a theorem due to G.V. Chudnovsky states that for x a non-zero algebraic number with |x|<1, then 2F1(1/2,1/2,1;x) and 2F1(-1/2,1/2,1;x) are algebraically independent over Q. */ }
int mpfr_exp_3 (mpfr_ptr y, mpfr_srcptr x, mp_rnd_t rnd_mode) { mpfr_t t, x_copy, tmp; mpz_t uk; mp_exp_t ttt, shift_x; unsigned long twopoweri; mpz_t *P; mp_prec_t *mult; int i, k, loop; int prec_x; mp_prec_t realprec, Prec; int iter; int inexact = 0; MPFR_SAVE_EXPO_DECL (expo); MPFR_ZIV_DECL (ziv_loop); MPFR_LOG_FUNC (("x[%#R]=%R rnd=%d", x, x, rnd_mode), ("y[%#R]=%R inexact=%d", y, y, inexact)); MPFR_SAVE_EXPO_MARK (expo); /* decompose x */ /* we first write x = 1.xxxxxxxxxxxxx ----- k bits -- */ prec_x = MPFR_INT_CEIL_LOG2 (MPFR_PREC (x)) - MPFR_LOG2_BITS_PER_MP_LIMB; if (prec_x < 0) prec_x = 0; ttt = MPFR_GET_EXP (x); mpfr_init2 (x_copy, MPFR_PREC(x)); mpfr_set (x_copy, x, GMP_RNDD); /* we shift to get a number less than 1 */ if (ttt > 0) { shift_x = ttt; mpfr_div_2ui (x_copy, x, ttt, GMP_RNDN); ttt = MPFR_GET_EXP (x_copy); } else shift_x = 0; MPFR_ASSERTD (ttt <= 0); /* Init prec and vars */ realprec = MPFR_PREC (y) + MPFR_INT_CEIL_LOG2 (prec_x + MPFR_PREC (y)); Prec = realprec + shift + 2 + shift_x; mpfr_init2 (t, Prec); mpfr_init2 (tmp, Prec); mpz_init (uk); /* Main loop */ MPFR_ZIV_INIT (ziv_loop, realprec); for (;;) { int scaled = 0; MPFR_BLOCK_DECL (flags); k = MPFR_INT_CEIL_LOG2 (Prec) - MPFR_LOG2_BITS_PER_MP_LIMB; /* now we have to extract */ twopoweri = BITS_PER_MP_LIMB; /* Allocate tables */ P = (mpz_t*) (*__gmp_allocate_func) (3*(k+2)*sizeof(mpz_t)); for (i = 0; i < 3*(k+2); i++) mpz_init (P[i]); mult = (mp_prec_t*) (*__gmp_allocate_func) (2*(k+2)*sizeof(mp_prec_t)); /* Particular case for i==0 */ mpfr_extract (uk, x_copy, 0); MPFR_ASSERTD (mpz_cmp_ui (uk, 0) != 0); mpfr_exp_rational (tmp, uk, shift + twopoweri - ttt, k + 1, P, mult); for (loop = 0; loop < shift; loop++) mpfr_sqr (tmp, tmp, GMP_RNDD); twopoweri *= 2; /* General case */ iter = (k <= prec_x) ? k : prec_x; for (i = 1; i <= iter; i++) { mpfr_extract (uk, x_copy, i); if (MPFR_LIKELY (mpz_cmp_ui (uk, 0) != 0)) { mpfr_exp_rational (t, uk, twopoweri - ttt, k - i + 1, P, mult); mpfr_mul (tmp, tmp, t, GMP_RNDD); } MPFR_ASSERTN (twopoweri <= LONG_MAX/2); twopoweri *=2; } /* Clear tables */ for (i = 0; i < 3*(k+2); i++) mpz_clear (P[i]); (*__gmp_free_func) (P, 3*(k+2)*sizeof(mpz_t)); (*__gmp_free_func) (mult, 2*(k+2)*sizeof(mp_prec_t)); if (shift_x > 0) { MPFR_BLOCK (flags, { for (loop = 0; loop < shift_x - 1; loop++) mpfr_sqr (tmp, tmp, GMP_RNDD); mpfr_sqr (t, tmp, GMP_RNDD); } ); if (MPFR_UNLIKELY (MPFR_OVERFLOW (flags))) { /* tmp <= exact result, so that it is a real overflow. */ inexact = mpfr_overflow (y, rnd_mode, 1); MPFR_SAVE_EXPO_UPDATE_FLAGS (expo, MPFR_FLAGS_OVERFLOW); break; } if (MPFR_UNLIKELY (MPFR_UNDERFLOW (flags))) { /* This may be a spurious underflow. So, let's scale the result. */ mpfr_mul_2ui (tmp, tmp, 1, GMP_RNDD); /* no overflow, exact */ mpfr_sqr (t, tmp, GMP_RNDD); if (MPFR_IS_ZERO (t)) { /* approximate result < 2^(emin - 3), thus exact result < 2^(emin - 2). */ inexact = mpfr_underflow (y, (rnd_mode == GMP_RNDN) ? GMP_RNDZ : rnd_mode, 1); MPFR_SAVE_EXPO_UPDATE_FLAGS (expo, MPFR_FLAGS_UNDERFLOW); break; } scaled = 1; } }
int mpfr_asin (mpfr_ptr asin, mpfr_srcptr x, mpfr_rnd_t rnd_mode) { mpfr_t xp; int compared, inexact; mpfr_prec_t prec; mpfr_exp_t xp_exp; MPFR_SAVE_EXPO_DECL (expo); MPFR_ZIV_DECL (loop); MPFR_LOG_FUNC ( ("x[%Pu]=%.*Rg rnd=%d", mpfr_get_prec (x), mpfr_log_prec, x, rnd_mode), ("asin[%Pu]=%.*Rg inexact=%d", mpfr_get_prec (asin), mpfr_log_prec, asin, inexact)); /* Special cases */ if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x))) { if (MPFR_IS_NAN (x) || MPFR_IS_INF (x)) { MPFR_SET_NAN (asin); MPFR_RET_NAN; } else /* x = 0 */ { MPFR_ASSERTD (MPFR_IS_ZERO (x)); MPFR_SET_ZERO (asin); MPFR_SET_SAME_SIGN (asin, x); MPFR_RET (0); /* exact result */ } } /* asin(x) = x + x^3/6 + ... so the error is < 2^(3*EXP(x)-2) */ MPFR_FAST_COMPUTE_IF_SMALL_INPUT (asin, x, -2 * MPFR_GET_EXP (x), 2, 1, rnd_mode, {}); /* Set x_p=|x| (x is a normal number) */ mpfr_init2 (xp, MPFR_PREC (x)); inexact = mpfr_abs (xp, x, MPFR_RNDN); MPFR_ASSERTD (inexact == 0); compared = mpfr_cmp_ui (xp, 1); MPFR_SAVE_EXPO_MARK (expo); if (MPFR_UNLIKELY (compared >= 0)) { mpfr_clear (xp); if (compared > 0) /* asin(x) = NaN for |x| > 1 */ { MPFR_SAVE_EXPO_FREE (expo); MPFR_SET_NAN (asin); MPFR_RET_NAN; } else /* x = 1 or x = -1 */ { if (MPFR_IS_POS (x)) /* asin(+1) = Pi/2 */ inexact = mpfr_const_pi (asin, rnd_mode); else /* asin(-1) = -Pi/2 */ { inexact = -mpfr_const_pi (asin, MPFR_INVERT_RND(rnd_mode)); MPFR_CHANGE_SIGN (asin); } mpfr_div_2ui (asin, asin, 1, rnd_mode); } } else { /* Compute exponent of 1 - ABS(x) */ mpfr_ui_sub (xp, 1, xp, MPFR_RNDD); MPFR_ASSERTD (MPFR_GET_EXP (xp) <= 0); MPFR_ASSERTD (MPFR_GET_EXP (x) <= 0); xp_exp = 2 - MPFR_GET_EXP (xp); /* Set up initial prec */ prec = MPFR_PREC (asin) + 10 + xp_exp; /* use asin(x) = atan(x/sqrt(1-x^2)) */ MPFR_ZIV_INIT (loop, prec); for (;;) { mpfr_set_prec (xp, prec); mpfr_sqr (xp, x, MPFR_RNDN); mpfr_ui_sub (xp, 1, xp, MPFR_RNDN); mpfr_sqrt (xp, xp, MPFR_RNDN); mpfr_div (xp, x, xp, MPFR_RNDN); mpfr_atan (xp, xp, MPFR_RNDN); if (MPFR_LIKELY (MPFR_CAN_ROUND (xp, prec - xp_exp, MPFR_PREC (asin), rnd_mode))) break; MPFR_ZIV_NEXT (loop, prec); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (asin, xp, rnd_mode); mpfr_clear (xp); } MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (asin, inexact, rnd_mode); }
int mpfr_cos (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode) { mpfr_prec_t K0, K, precy, m, k, l; int inexact, reduce = 0; mpfr_t r, s, xr, c; mpfr_exp_t exps, cancel = 0, expx; MPFR_ZIV_DECL (loop); MPFR_SAVE_EXPO_DECL (expo); MPFR_GROUP_DECL (group); MPFR_LOG_FUNC ( ("x[%Pu]=%*.Rg rnd=%d", mpfr_get_prec (x), mpfr_log_prec, x, rnd_mode), ("y[%Pu]=%*.Rg inexact=%d", mpfr_get_prec (y), mpfr_log_prec, y, inexact)); if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x))) { if (MPFR_IS_NAN (x) || MPFR_IS_INF (x)) { MPFR_SET_NAN (y); MPFR_RET_NAN; } else { MPFR_ASSERTD (MPFR_IS_ZERO (x)); return mpfr_set_ui (y, 1, rnd_mode); } } MPFR_SAVE_EXPO_MARK (expo); /* cos(x) = 1-x^2/2 + ..., so error < 2^(2*EXP(x)-1) */ expx = MPFR_GET_EXP (x); MPFR_SMALL_INPUT_AFTER_SAVE_EXPO (y, __gmpfr_one, -2 * expx, 1, 0, rnd_mode, expo, {}); /* Compute initial precision */ precy = MPFR_PREC (y); if (precy >= MPFR_SINCOS_THRESHOLD) { MPFR_SAVE_EXPO_FREE (expo); return mpfr_cos_fast (y, x, rnd_mode); } K0 = __gmpfr_isqrt (precy / 3); m = precy + 2 * MPFR_INT_CEIL_LOG2 (precy) + 2 * K0; if (expx >= 3) { reduce = 1; /* As expx + m - 1 will silently be converted into mpfr_prec_t in the mpfr_init2 call, the assert below may be useful to avoid undefined behavior. */ MPFR_ASSERTN (expx + m - 1 <= MPFR_PREC_MAX); mpfr_init2 (c, expx + m - 1); mpfr_init2 (xr, m); } MPFR_GROUP_INIT_2 (group, m, r, s); MPFR_ZIV_INIT (loop, m); for (;;) { /* If |x| >= 4, first reduce x cmod (2*Pi) into xr, using mpfr_remainder: let e = EXP(x) >= 3, and m the target precision: (1) c <- 2*Pi [precision e+m-1, nearest] (2) xr <- remainder (x, c) [precision m, nearest] We have |c - 2*Pi| <= 1/2ulp(c) = 2^(3-e-m) |xr - x - k c| <= 1/2ulp(xr) <= 2^(1-m) |k| <= |x|/(2*Pi) <= 2^(e-2) Thus |xr - x - 2kPi| <= |k| |c - 2Pi| + 2^(1-m) <= 2^(2-m). It follows |cos(xr) - cos(x)| <= 2^(2-m). */ if (reduce) { mpfr_const_pi (c, MPFR_RNDN); mpfr_mul_2ui (c, c, 1, MPFR_RNDN); /* 2Pi */ mpfr_remainder (xr, x, c, MPFR_RNDN); if (MPFR_IS_ZERO(xr)) goto ziv_next; /* now |xr| <= 4, thus r <= 16 below */ mpfr_mul (r, xr, xr, MPFR_RNDU); /* err <= 1 ulp */ } else mpfr_mul (r, x, x, MPFR_RNDU); /* err <= 1 ulp */ /* now |x| < 4 (or xr if reduce = 1), thus |r| <= 16 */ /* we need |r| < 1/2 for mpfr_cos2_aux, i.e., EXP(r) - 2K <= -1 */ K = K0 + 1 + MAX(0, MPFR_GET_EXP(r)) / 2; /* since K0 >= 0, if EXP(r) < 0, then K >= 1, thus EXP(r) - 2K <= -3; otherwise if EXP(r) >= 0, then K >= 1/2 + EXP(r)/2, thus EXP(r) - 2K <= -1 */ MPFR_SET_EXP (r, MPFR_GET_EXP (r) - 2 * K); /* Can't overflow! */ /* s <- 1 - r/2! + ... + (-1)^l r^l/(2l)! */ l = mpfr_cos2_aux (s, r); /* l is the error bound in ulps on s */ MPFR_SET_ONE (r); for (k = 0; k < K; k++) { mpfr_sqr (s, s, MPFR_RNDU); /* err <= 2*olderr */ MPFR_SET_EXP (s, MPFR_GET_EXP (s) + 1); /* Can't overflow */ mpfr_sub (s, s, r, MPFR_RNDN); /* err <= 4*olderr */ if (MPFR_IS_ZERO(s)) goto ziv_next; MPFR_ASSERTD (MPFR_GET_EXP (s) <= 1); } /* The absolute error on s is bounded by (2l+1/3)*2^(2K-m) 2l+1/3 <= 2l+1. If |x| >= 4, we need to add 2^(2-m) for the argument reduction by 2Pi: if K = 0, this amounts to add 4 to 2l+1/3, i.e., to add 2 to l; if K >= 1, this amounts to add 1 to 2*l+1/3. */ l = 2 * l + 1; if (reduce) l += (K == 0) ? 4 : 1; k = MPFR_INT_CEIL_LOG2 (l) + 2*K; /* now the error is bounded by 2^(k-m) = 2^(EXP(s)-err) */ exps = MPFR_GET_EXP (s); if (MPFR_LIKELY (MPFR_CAN_ROUND (s, exps + m - k, precy, rnd_mode))) break; if (MPFR_UNLIKELY (exps == 1)) /* s = 1 or -1, and except x=0 which was already checked above, cos(x) cannot be 1 or -1, so we can round if the error is less than 2^(-precy) for directed rounding, or 2^(-precy-1) for rounding to nearest. */ { if (m > k && (m - k >= precy + (rnd_mode == MPFR_RNDN))) { /* If round to nearest or away, result is s = 1 or -1, otherwise it is round(nexttoward (s, 0)). However in order to have the inexact flag correctly set below, we set |s| to 1 - 2^(-m) in all cases. */ mpfr_nexttozero (s); break; } } if (exps < cancel) { m += cancel - exps; cancel = exps; } ziv_next: MPFR_ZIV_NEXT (loop, m); MPFR_GROUP_REPREC_2 (group, m, r, s); if (reduce) { mpfr_set_prec (xr, m); mpfr_set_prec (c, expx + m - 1); } } MPFR_ZIV_FREE (loop); inexact = mpfr_set (y, s, rnd_mode); MPFR_GROUP_CLEAR (group); if (reduce) { mpfr_clear (xr); mpfr_clear (c); } MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (y, inexact, rnd_mode); }
int mpfr_acos (mpfr_ptr acos, mpfr_srcptr x, mpfr_rnd_t rnd_mode) { mpfr_t xp, arcc, tmp; mpfr_exp_t supplement; mpfr_prec_t prec; int sign, compared, inexact; MPFR_SAVE_EXPO_DECL (expo); MPFR_ZIV_DECL (loop); MPFR_LOG_FUNC (("x[%Pu]=%.*Rg rnd=%d", mpfr_get_prec(x), mpfr_log_prec, x, rnd_mode), ("acos[%Pu]=%.*Rg inexact=%d", mpfr_get_prec(acos), mpfr_log_prec, acos, inexact)); /* Singular cases */ if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x))) { if (MPFR_IS_NAN (x) || MPFR_IS_INF (x)) { MPFR_SET_NAN (acos); MPFR_RET_NAN; } else /* necessarily x=0 */ { MPFR_ASSERTD(MPFR_IS_ZERO(x)); /* acos(0)=Pi/2 */ MPFR_SAVE_EXPO_MARK (expo); inexact = mpfr_const_pi (acos, rnd_mode); mpfr_div_2ui (acos, acos, 1, rnd_mode); /* exact */ MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (acos, inexact, rnd_mode); } } /* Set x_p=|x| */ sign = MPFR_SIGN (x); mpfr_init2 (xp, MPFR_PREC (x)); mpfr_abs (xp, x, MPFR_RNDN); /* Exact */ compared = mpfr_cmp_ui (xp, 1); if (MPFR_UNLIKELY (compared >= 0)) { mpfr_clear (xp); if (compared > 0) /* acos(x) = NaN for x > 1 */ { MPFR_SET_NAN(acos); MPFR_RET_NAN; } else { if (MPFR_IS_POS_SIGN (sign)) /* acos(+1) = +0 */ return mpfr_set_ui (acos, 0, rnd_mode); else /* acos(-1) = Pi */ return mpfr_const_pi (acos, rnd_mode); } } MPFR_SAVE_EXPO_MARK (expo); /* Compute the supplement */ mpfr_ui_sub (xp, 1, xp, MPFR_RNDD); if (MPFR_IS_POS_SIGN (sign)) supplement = 2 - 2 * MPFR_GET_EXP (xp); else supplement = 2 - MPFR_GET_EXP (xp); mpfr_clear (xp); prec = MPFR_PREC (acos); prec += MPFR_INT_CEIL_LOG2(prec) + 10 + supplement; /* VL: The following change concerning prec comes from r3145 "Optimize mpfr_acos by choosing a better initial precision." but it doesn't seem to be correct and leads to problems (assertion failure or very important inefficiency) with tiny arguments. Therefore, I've disabled it. */ /* If x ~ 2^-N, acos(x) ~ PI/2 - x - x^3/6 If Prec < 2*N, we can't round since x^3/6 won't be counted. */ #if 0 if (MPFR_PREC (acos) >= MPFR_PREC (x) && MPFR_GET_EXP (x) < 0) { mpfr_uexp_t pmin = (mpfr_uexp_t) (-2 * MPFR_GET_EXP (x)) + 5; MPFR_ASSERTN (pmin <= MPFR_PREC_MAX); if (prec < pmin) prec = pmin; } #endif mpfr_init2 (tmp, prec); mpfr_init2 (arcc, prec); MPFR_ZIV_INIT (loop, prec); for (;;) { /* acos(x) = Pi/2 - asin(x) = Pi/2 - atan(x/sqrt(1-x^2)) */ mpfr_sqr (tmp, x, MPFR_RNDN); mpfr_ui_sub (tmp, 1, tmp, MPFR_RNDN); mpfr_sqrt (tmp, tmp, MPFR_RNDN); mpfr_div (tmp, x, tmp, MPFR_RNDN); mpfr_atan (arcc, tmp, MPFR_RNDN); mpfr_const_pi (tmp, MPFR_RNDN); mpfr_div_2ui (tmp, tmp, 1, MPFR_RNDN); mpfr_sub (arcc, tmp, arcc, MPFR_RNDN); if (MPFR_LIKELY (MPFR_CAN_ROUND (arcc, prec - supplement, MPFR_PREC (acos), rnd_mode))) break; MPFR_ZIV_NEXT (loop, prec); mpfr_set_prec (tmp, prec); mpfr_set_prec (arcc, prec); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (acos, arcc, rnd_mode); mpfr_clear (tmp); mpfr_clear (arcc); MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (acos, inexact, rnd_mode); }
int mpc_sqr (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) { int ok; mpfr_t u, v; mpfr_t x; /* temporary variable to hold the real part of op, needed in the case rop==op */ mpfr_prec_t prec; int inex_re, inex_im, inexact; mpfr_exp_t emin; int saved_underflow; /* special values: NaN and infinities */ if (!mpc_fin_p (op)) { if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op))) { mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); } else if (mpfr_inf_p (mpc_realref (op))) { if (mpfr_inf_p (mpc_imagref (op))) { mpfr_set_inf (mpc_imagref (rop), MPFR_SIGN (mpc_realref (op)) * MPFR_SIGN (mpc_imagref (op))); mpfr_set_nan (mpc_realref (rop)); } else { if (mpfr_zero_p (mpc_imagref (op))) mpfr_set_nan (mpc_imagref (rop)); else mpfr_set_inf (mpc_imagref (rop), MPFR_SIGN (mpc_realref (op)) * MPFR_SIGN (mpc_imagref (op))); mpfr_set_inf (mpc_realref (rop), +1); } } else /* IM(op) is infinity, RE(op) is not */ { if (mpfr_zero_p (mpc_realref (op))) mpfr_set_nan (mpc_imagref (rop)); else mpfr_set_inf (mpc_imagref (rop), MPFR_SIGN (mpc_realref (op)) * MPFR_SIGN (mpc_imagref (op))); mpfr_set_inf (mpc_realref (rop), -1); } return MPC_INEX (0, 0); /* exact */ } prec = MPC_MAX_PREC(rop); /* Check for real resp. purely imaginary number */ if (mpfr_zero_p (mpc_imagref(op))) { int same_sign = mpfr_signbit (mpc_realref (op)) == mpfr_signbit (mpc_imagref (op)); inex_re = mpfr_sqr (mpc_realref(rop), mpc_realref(op), MPC_RND_RE(rnd)); inex_im = mpfr_set_ui (mpc_imagref(rop), 0ul, MPFR_RNDN); if (!same_sign) mpc_conj (rop, rop, MPC_RNDNN); return MPC_INEX(inex_re, inex_im); } if (mpfr_zero_p (mpc_realref(op))) { int same_sign = mpfr_signbit (mpc_realref (op)) == mpfr_signbit (mpc_imagref (op)); inex_re = -mpfr_sqr (mpc_realref(rop), mpc_imagref(op), INV_RND (MPC_RND_RE(rnd))); mpfr_neg (mpc_realref(rop), mpc_realref(rop), MPFR_RNDN); inex_im = mpfr_set_ui (mpc_imagref(rop), 0ul, MPFR_RNDN); if (!same_sign) mpc_conj (rop, rop, MPC_RNDNN); return MPC_INEX(inex_re, inex_im); } if (rop == op) { mpfr_init2 (x, MPC_PREC_RE (op)); mpfr_set (x, op->re, MPFR_RNDN); } else x [0] = op->re [0]; /* From here on, use x instead of op->re and safely overwrite rop->re. */ /* Compute real part of result. */ if (SAFE_ABS (mpfr_exp_t, mpfr_get_exp (mpc_realref (op)) - mpfr_get_exp (mpc_imagref (op))) > (mpfr_exp_t) MPC_MAX_PREC (op) / 2) { /* If the real and imaginary parts of the argument have very different exponents, it is not reasonable to use Karatsuba squaring; compute exactly with the standard formulae instead, even if this means an additional multiplication. Using the approach copied from mul, over- and underflows are also handled correctly. */ inex_re = mpfr_fsss (rop->re, x, op->im, MPC_RND_RE (rnd)); } else { /* Karatsuba squaring: we compute the real part as (x+y)*(x-y) and the imaginary part as 2*x*y, with a total of 2M instead of 2S+1M for the naive algorithm, which computes x^2-y^2 and 2*y*y */ mpfr_init (u); mpfr_init (v); emin = mpfr_get_emin (); do { prec += mpc_ceil_log2 (prec) + 5; mpfr_set_prec (u, prec); mpfr_set_prec (v, prec); /* Let op = x + iy. We need u = x+y and v = x-y, rounded away. */ /* The error is bounded above by 1 ulp. */ /* We first let inexact be 1 if the real part is not computed */ /* exactly and determine the sign later. */ inexact = mpfr_add (u, x, mpc_imagref (op), MPFR_RNDA) | mpfr_sub (v, x, mpc_imagref (op), MPFR_RNDA); /* compute the real part as u*v, rounded away */ /* determine also the sign of inex_re */ if (mpfr_sgn (u) == 0 || mpfr_sgn (v) == 0) { /* as we have rounded away, the result is exact */ mpfr_set_ui (mpc_realref (rop), 0, MPFR_RNDN); inex_re = 0; ok = 1; } else { inexact |= mpfr_mul (u, u, v, MPFR_RNDA); /* error 5 */ if (mpfr_get_exp (u) == emin || mpfr_inf_p (u)) { /* under- or overflow */ inex_re = mpfr_fsss (rop->re, x, op->im, MPC_RND_RE (rnd)); ok = 1; } else { ok = (!inexact) | mpfr_can_round (u, prec - 3, MPFR_RNDA, MPFR_RNDZ, MPC_PREC_RE (rop) + (MPC_RND_RE (rnd) == MPFR_RNDN)); if (ok) { inex_re = mpfr_set (mpc_realref (rop), u, MPC_RND_RE (rnd)); if (inex_re == 0) /* remember that u was already rounded */ inex_re = inexact; } } } } while (!ok); mpfr_clear (u); mpfr_clear (v); } saved_underflow = mpfr_underflow_p (); mpfr_clear_underflow (); inex_im = mpfr_mul (rop->im, x, op->im, MPC_RND_IM (rnd)); if (!mpfr_underflow_p ()) inex_im |= mpfr_mul_2ui (rop->im, rop->im, 1, MPC_RND_IM (rnd)); /* We must not multiply by 2 if rop->im has been set to the smallest representable number. */ if (saved_underflow) mpfr_set_underflow (); if (rop == op) mpfr_clear (x); return MPC_INEX (inex_re, inex_im); }
static int mpfr_fsss (mpfr_ptr z, mpfr_srcptr a, mpfr_srcptr c, mpfr_rnd_t rnd) { /* Computes z = a^2 - c^2. Assumes that a and c are finite and non-zero; so a squaring yielding an infinity is an overflow, and a squaring yielding 0 is an underflow. Assumes further that z is distinct from a and c. */ int inex; mpfr_t u, v; /* u=a^2, v=c^2 exactly */ mpfr_init2 (u, 2*mpfr_get_prec (a)); mpfr_init2 (v, 2*mpfr_get_prec (c)); mpfr_sqr (u, a, MPFR_RNDN); mpfr_sqr (v, c, MPFR_RNDN); /* tentatively compute z as u-v; here we need z to be distinct from a and c to not lose the latter */ inex = mpfr_sub (z, u, v, rnd); if (mpfr_inf_p (z)) { /* replace by "correctly rounded overflow" */ mpfr_set_si (z, (mpfr_signbit (z) ? -1 : 1), MPFR_RNDN); inex = mpfr_mul_2ui (z, z, mpfr_get_emax (), rnd); } else if (mpfr_zero_p (u) && !mpfr_zero_p (v)) { /* exactly u underflowed, determine inexact flag */ inex = (mpfr_signbit (u) ? 1 : -1); } else if (mpfr_zero_p (v) && !mpfr_zero_p (u)) { /* exactly v underflowed, determine inexact flag */ inex = (mpfr_signbit (v) ? -1 : 1); } else if (mpfr_nan_p (z) || (mpfr_zero_p (u) && mpfr_zero_p (v))) { /* In the first case, u and v are +inf. In the second case, u and v are zeroes; their difference may be 0 or the least representable number, with a sign to be determined. Redo the computations with mpz_t exponents */ mpfr_exp_t ea, ec; mpz_t eu, ev; /* cheat to work around the const qualifiers */ /* Normalise the input by shifting and keep track of the shifts in the exponents of u and v */ ea = mpfr_get_exp (a); ec = mpfr_get_exp (c); mpfr_set_exp ((mpfr_ptr) a, (mpfr_prec_t) 0); mpfr_set_exp ((mpfr_ptr) c, (mpfr_prec_t) 0); mpz_init (eu); mpz_init (ev); mpz_set_si (eu, (long int) ea); mpz_mul_2exp (eu, eu, 1); mpz_set_si (ev, (long int) ec); mpz_mul_2exp (ev, ev, 1); /* recompute u and v and move exponents to eu and ev */ mpfr_sqr (u, a, MPFR_RNDN); /* exponent of u is non-positive */ mpz_sub_ui (eu, eu, (unsigned long int) (-mpfr_get_exp (u))); mpfr_set_exp (u, (mpfr_prec_t) 0); mpfr_sqr (v, c, MPFR_RNDN); mpz_sub_ui (ev, ev, (unsigned long int) (-mpfr_get_exp (v))); mpfr_set_exp (v, (mpfr_prec_t) 0); if (mpfr_nan_p (z)) { mpfr_exp_t emax = mpfr_get_emax (); int overflow; /* We have a = ma * 2^ea with 1/2 <= |ma| < 1 and ea <= emax. So eu <= 2*emax, and eu > emax since we have an overflow. The same holds for ev. Shift u and v by as much as possible so that one of them has exponent emax and the remaining exponents in eu and ev are the same. Then carry out the addition. Shifting u and v prevents an underflow. */ if (mpz_cmp (eu, ev) >= 0) { mpfr_set_exp (u, emax); mpz_sub_ui (eu, eu, (long int) emax); mpz_sub (ev, ev, eu); mpfr_set_exp (v, (mpfr_exp_t) mpz_get_ui (ev)); /* remaining common exponent is now in eu */ } else { mpfr_set_exp (v, emax); mpz_sub_ui (ev, ev, (long int) emax); mpz_sub (eu, eu, ev); mpfr_set_exp (u, (mpfr_exp_t) mpz_get_ui (eu)); mpz_set (eu, ev); /* remaining common exponent is now also in eu */ } inex = mpfr_sub (z, u, v, rnd); /* Result is finite since u and v have the same sign. */ overflow = mpfr_mul_2ui (z, z, mpz_get_ui (eu), rnd); if (overflow) inex = overflow; } else { int underflow; /* Subtraction of two zeroes. We have a = ma * 2^ea with 1/2 <= |ma| < 1 and ea >= emin and similarly for b. So 2*emin < 2*emin+1 <= eu < emin < 0, and analogously for v. */ mpfr_exp_t emin = mpfr_get_emin (); if (mpz_cmp (eu, ev) <= 0) { mpfr_set_exp (u, emin); mpz_add_ui (eu, eu, (unsigned long int) (-emin)); mpz_sub (ev, ev, eu); mpfr_set_exp (v, (mpfr_exp_t) mpz_get_si (ev)); } else { mpfr_set_exp (v, emin); mpz_add_ui (ev, ev, (unsigned long int) (-emin)); mpz_sub (eu, eu, ev); mpfr_set_exp (u, (mpfr_exp_t) mpz_get_si (eu)); mpz_set (eu, ev); } inex = mpfr_sub (z, u, v, rnd); mpz_neg (eu, eu); underflow = mpfr_div_2ui (z, z, mpz_get_ui (eu), rnd); if (underflow) inex = underflow; } mpz_clear (eu); mpz_clear (ev); mpfr_set_exp ((mpfr_ptr) a, ea); mpfr_set_exp ((mpfr_ptr) c, ec); /* works also when a == c */ } mpfr_clear (u); mpfr_clear (v); return inex; }
int mpc_log (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd){ int ok, underflow = 0; mpfr_srcptr x, y; mpfr_t v, w; mpfr_prec_t prec; int loops; int re_cmp, im_cmp; int inex_re, inex_im; int err; mpfr_exp_t expw; int sgnw; /* special values: NaN and infinities */ if (!mpc_fin_p (op)) { if (mpfr_nan_p (mpc_realref (op))) { if (mpfr_inf_p (mpc_imagref (op))) mpfr_set_inf (mpc_realref (rop), +1); else mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); inex_im = 0; /* Inf/NaN is exact */ } else if (mpfr_nan_p (mpc_imagref (op))) { if (mpfr_inf_p (mpc_realref (op))) mpfr_set_inf (mpc_realref (rop), +1); else mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); inex_im = 0; /* Inf/NaN is exact */ } else /* We have an infinity in at least one part. */ { inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op), MPC_RND_IM (rnd)); mpfr_set_inf (mpc_realref (rop), +1); } return MPC_INEX(0, inex_im); } /* special cases: real and purely imaginary numbers */ re_cmp = mpfr_cmp_ui (mpc_realref (op), 0); im_cmp = mpfr_cmp_ui (mpc_imagref (op), 0); if (im_cmp == 0) { if (re_cmp == 0) { inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op), MPC_RND_IM (rnd)); mpfr_set_inf (mpc_realref (rop), -1); inex_re = 0; /* -Inf is exact */ } else if (re_cmp > 0) { inex_re = mpfr_log (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); inex_im = mpfr_set (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd)); } else { /* op = x + 0*y; let w = -x = |x| */ int negative_zero; mpfr_rnd_t rnd_im; negative_zero = mpfr_signbit (mpc_imagref (op)); if (negative_zero) rnd_im = INV_RND (MPC_RND_IM (rnd)); else rnd_im = MPC_RND_IM (rnd); w [0] = *mpc_realref (op); MPFR_CHANGE_SIGN (w); inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd)); inex_im = mpfr_const_pi (mpc_imagref (rop), rnd_im); if (negative_zero) { mpc_conj (rop, rop, MPC_RNDNN); inex_im = -inex_im; } } return MPC_INEX(inex_re, inex_im); } else if (re_cmp == 0) { if (im_cmp > 0) { inex_re = mpfr_log (mpc_realref (rop), mpc_imagref (op), MPC_RND_RE (rnd)); inex_im = mpfr_const_pi (mpc_imagref (rop), MPC_RND_IM (rnd)); /* division by 2 does not change the ternary flag */ mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN); } else { w [0] = *mpc_imagref (op); MPFR_CHANGE_SIGN (w); inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd)); inex_im = mpfr_const_pi (mpc_imagref (rop), INV_RND (MPC_RND_IM (rnd))); /* division by 2 does not change the ternary flag */ mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN); mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), GMP_RNDN); inex_im = -inex_im; /* negate the ternary flag */ } return MPC_INEX(inex_re, inex_im); } prec = MPC_PREC_RE(rop); mpfr_init2 (w, 2); /* let op = x + iy; log = 1/2 log (x^2 + y^2) + i atan2 (y, x) */ /* loop for the real part: 1/2 log (x^2 + y^2), fast, but unsafe */ /* implementation */ ok = 0; for (loops = 1; !ok && loops <= 2; loops++) { prec += mpc_ceil_log2 (prec) + 4; mpfr_set_prec (w, prec); mpc_abs (w, op, GMP_RNDN); /* error 0.5 ulp */ if (mpfr_inf_p (w)) /* intermediate overflow; the logarithm may be representable. Intermediate underflow is impossible. */ break; mpfr_log (w, w, GMP_RNDN); /* generic error of log: (2^(- exp(w)) + 0.5) ulp */ if (mpfr_zero_p (w)) /* impossible to round, switch to second algorithm */ break; err = MPC_MAX (-mpfr_get_exp (w), 0) + 1; /* number of lost digits */ ok = mpfr_can_round (w, prec - err, GMP_RNDN, GMP_RNDZ, mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == GMP_RNDN)); } if (!ok) { prec = MPC_PREC_RE(rop); mpfr_init2 (v, 2); /* compute 1/2 log (x^2 + y^2) = log |x| + 1/2 * log (1 + (y/x)^2) if |x| >= |y|; otherwise, exchange x and y */ if (mpfr_cmpabs (mpc_realref (op), mpc_imagref (op)) >= 0) { x = mpc_realref (op); y = mpc_imagref (op); } else { x = mpc_imagref (op); y = mpc_realref (op); } do { prec += mpc_ceil_log2 (prec) + 4; mpfr_set_prec (v, prec); mpfr_set_prec (w, prec); mpfr_div (v, y, x, GMP_RNDD); /* error 1 ulp */ mpfr_sqr (v, v, GMP_RNDD); /* generic error of multiplication: 1 + 2*1*(2+1*2^(1-prec)) <= 5.0625 since prec >= 6 */ mpfr_log1p (v, v, GMP_RNDD); /* error 1 + 4*5.0625 = 21.25 , see algorithms.tex */ mpfr_div_2ui (v, v, 1, GMP_RNDD); /* If the result is 0, then there has been an underflow somewhere. */ mpfr_abs (w, x, GMP_RNDN); /* exact */ mpfr_log (w, w, GMP_RNDN); /* error 0.5 ulp */ expw = mpfr_get_exp (w); sgnw = mpfr_signbit (w); mpfr_add (w, w, v, GMP_RNDN); if (!sgnw) /* v is positive, so no cancellation; error 22.25 ulp; error counts lost bits */ err = 5; else err = MPC_MAX (5 + mpfr_get_exp (v), /* 21.25 ulp (v) rewritten in ulp (result, now in w) */ -1 + expw - mpfr_get_exp (w) /* 0.5 ulp (previous w), rewritten in ulp (result) */ ) + 2; /* handle one special case: |x|=1, and (y/x)^2 underflows; then 1/2*log(x^2+y^2) \approx 1/2*y^2 also underflows. */ if ( (mpfr_cmp_si (x, -1) == 0 || mpfr_cmp_ui (x, 1) == 0) && mpfr_zero_p (w)) underflow = 1; } while (!underflow && !mpfr_can_round (w, prec - err, GMP_RNDN, GMP_RNDZ, mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == GMP_RNDN))); mpfr_clear (v); } /* imaginary part */ inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op), MPC_RND_IM (rnd)); /* set the real part; cannot be done before if rop==op */ if (underflow) /* create underflow in result */ inex_re = mpfr_set_ui_2exp (mpc_realref (rop), 1, mpfr_get_emin_min () - 2, MPC_RND_RE (rnd)); else inex_re = mpfr_set (mpc_realref (rop), w, MPC_RND_RE (rnd)); mpfr_clear (w); return MPC_INEX(inex_re, inex_im); }
/* Compute the real part of the dilogarithm defined by Li2(x) = -\Int_{t=0}^x log(1-t)/t dt */ int mpfr_li2 (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode) { int inexact; mp_exp_t err; mpfr_prec_t yp, m; MPFR_ZIV_DECL (loop); MPFR_SAVE_EXPO_DECL (expo); MPFR_LOG_FUNC (("x[%#R]=%R rnd=%d", x, x, rnd_mode), ("y[%#R]=%R", y)); if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x))) { if (MPFR_IS_NAN (x)) { MPFR_SET_NAN (y); MPFR_RET_NAN; } else if (MPFR_IS_INF (x)) { MPFR_SET_NEG (y); MPFR_SET_INF (y); MPFR_RET (0); } else /* x is zero */ { MPFR_ASSERTD (MPFR_IS_ZERO (x)); MPFR_SET_SAME_SIGN (y, x); MPFR_SET_ZERO (y); MPFR_RET (0); } } /* Li2(x) = x + x^2/4 + x^3/9 + ..., more precisely for 0 < x <= 1/2 we have |Li2(x) - x| < x^2/2 <= 2^(2EXP(x)-1) and for -1/2 <= x < 0 we have |Li2(x) - x| < x^2/4 <= 2^(2EXP(x)-2) */ if (MPFR_IS_POS (x)) MPFR_FAST_COMPUTE_IF_SMALL_INPUT (y, x, -MPFR_GET_EXP (x), 1, 1, rnd_mode, {}); else MPFR_FAST_COMPUTE_IF_SMALL_INPUT (y, x, -MPFR_GET_EXP (x), 2, 0, rnd_mode, {}); MPFR_SAVE_EXPO_MARK (expo); yp = MPFR_PREC (y); m = yp + MPFR_INT_CEIL_LOG2 (yp) + 13; if (MPFR_LIKELY ((mpfr_cmp_ui (x, 0) > 0) && (mpfr_cmp_d (x, 0.5) <= 0))) /* 0 < x <= 1/2: Li2(x) = S(-log(1-x))-log^2(1-x)/4 */ { mpfr_t s, u; mp_exp_t expo_l; int k; mpfr_init2 (u, m); mpfr_init2 (s, m); MPFR_ZIV_INIT (loop, m); for (;;) { mpfr_ui_sub (u, 1, x, GMP_RNDN); mpfr_log (u, u, GMP_RNDU); if (MPFR_IS_ZERO(u)) goto next_m; mpfr_neg (u, u, GMP_RNDN); /* u = -log(1-x) */ expo_l = MPFR_GET_EXP (u); k = li2_series (s, u, GMP_RNDU); err = 1 + MPFR_INT_CEIL_LOG2 (k + 1); mpfr_sqr (u, u, GMP_RNDU); mpfr_div_2ui (u, u, 2, GMP_RNDU); /* u = log^2(1-x) / 4 */ mpfr_sub (s, s, u, GMP_RNDN); /* error(s) <= (0.5 + 2^(d-EXP(s)) + 2^(3 + MAX(1, - expo_l) - EXP(s))) ulp(s) */ err = MAX (err, MAX (1, - expo_l) - 1) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err); if (MPFR_CAN_ROUND (s, (mp_exp_t) m - err, yp, rnd_mode)) break; next_m: MPFR_ZIV_NEXT (loop, m); mpfr_set_prec (u, m); mpfr_set_prec (s, m); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (y, s, rnd_mode); mpfr_clear (u); mpfr_clear (s); MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (y, inexact, rnd_mode); } else if (!mpfr_cmp_ui (x, 1)) /* Li2(1)= pi^2 / 6 */ { mpfr_t u; mpfr_init2 (u, m); MPFR_ZIV_INIT (loop, m); for (;;) { mpfr_const_pi (u, GMP_RNDU); mpfr_sqr (u, u, GMP_RNDN); mpfr_div_ui (u, u, 6, GMP_RNDN); err = m - 4; /* error(u) <= 19/2 ulp(u) */ if (MPFR_CAN_ROUND (u, err, yp, rnd_mode)) break; MPFR_ZIV_NEXT (loop, m); mpfr_set_prec (u, m); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (y, u, rnd_mode); mpfr_clear (u); MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (y, inexact, rnd_mode); } else if (mpfr_cmp_ui (x, 2) >= 0) /* x >= 2: Li2(x) = -S(-log(1-1/x))-log^2(x)/2+log^2(1-1/x)/4+pi^2/3 */ { int k; mp_exp_t expo_l; mpfr_t s, u, xx; if (mpfr_cmp_ui (x, 38) >= 0) { inexact = mpfr_li2_asympt_pos (y, x, rnd_mode); if (inexact != 0) goto end_of_case_gt2; } mpfr_init2 (u, m); mpfr_init2 (s, m); mpfr_init2 (xx, m); MPFR_ZIV_INIT (loop, m); for (;;) { mpfr_ui_div (xx, 1, x, GMP_RNDN); mpfr_neg (xx, xx, GMP_RNDN); mpfr_log1p (u, xx, GMP_RNDD); mpfr_neg (u, u, GMP_RNDU); /* u = -log(1-1/x) */ expo_l = MPFR_GET_EXP (u); k = li2_series (s, u, GMP_RNDN); mpfr_neg (s, s, GMP_RNDN); err = MPFR_INT_CEIL_LOG2 (k + 1) + 1; /* error(s) <= 2^err ulp(s) */ mpfr_sqr (u, u, GMP_RNDN); mpfr_div_2ui (u, u, 2, GMP_RNDN); /* u= log^2(1-1/x)/4 */ mpfr_add (s, s, u, GMP_RNDN); err = MAX (err, 3 + MAX (1, -expo_l) + MPFR_GET_EXP (u)) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err); /* error(s) <= 2^err ulp(s) */ err += MPFR_GET_EXP (s); mpfr_log (u, x, GMP_RNDU); mpfr_sqr (u, u, GMP_RNDN); mpfr_div_2ui (u, u, 1, GMP_RNDN); /* u = log^2(x)/2 */ mpfr_sub (s, s, u, GMP_RNDN); err = MAX (err, 3 + MPFR_GET_EXP (u)) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err); /* error(s) <= 2^err ulp(s) */ err += MPFR_GET_EXP (s); mpfr_const_pi (u, GMP_RNDU); mpfr_sqr (u, u, GMP_RNDN); mpfr_div_ui (u, u, 3, GMP_RNDN); /* u = pi^2/3 */ mpfr_add (s, s, u, GMP_RNDN); err = MAX (err, 2) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err); /* error(s) <= 2^err ulp(s) */ if (MPFR_CAN_ROUND (s, (mp_exp_t) m - err, yp, rnd_mode)) break; MPFR_ZIV_NEXT (loop, m); mpfr_set_prec (u, m); mpfr_set_prec (s, m); mpfr_set_prec (xx, m); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (y, s, rnd_mode); mpfr_clears (s, u, xx, (mpfr_ptr) 0); end_of_case_gt2: MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (y, inexact, rnd_mode); } else if (mpfr_cmp_ui (x, 1) > 0) /* 2 > x > 1: Li2(x) = S(log(x))+log^2(x)/4-log(x)log(x-1)+pi^2/6 */ { int k; mp_exp_t e1, e2; mpfr_t s, u, v, xx; mpfr_init2 (s, m); mpfr_init2 (u, m); mpfr_init2 (v, m); mpfr_init2 (xx, m); MPFR_ZIV_INIT (loop, m); for (;;) { mpfr_log (v, x, GMP_RNDU); k = li2_series (s, v, GMP_RNDN); e1 = MPFR_GET_EXP (s); mpfr_sqr (u, v, GMP_RNDN); mpfr_div_2ui (u, u, 2, GMP_RNDN); /* u = log^2(x)/4 */ mpfr_add (s, s, u, GMP_RNDN); mpfr_sub_ui (xx, x, 1, GMP_RNDN); mpfr_log (u, xx, GMP_RNDU); e2 = MPFR_GET_EXP (u); mpfr_mul (u, v, u, GMP_RNDN); /* u = log(x) * log(x-1) */ mpfr_sub (s, s, u, GMP_RNDN); mpfr_const_pi (u, GMP_RNDU); mpfr_sqr (u, u, GMP_RNDN); mpfr_div_ui (u, u, 6, GMP_RNDN); /* u = pi^2/6 */ mpfr_add (s, s, u, GMP_RNDN); /* error(s) <= (31 + (k+1) * 2^(1-e1) + 2^(1-e2)) ulp(s) see algorithms.tex */ err = MAX (MPFR_INT_CEIL_LOG2 (k + 1) + 1 - e1, 1 - e2); err = 2 + MAX (5, err); if (MPFR_CAN_ROUND (s, (mp_exp_t) m - err, yp, rnd_mode)) break; MPFR_ZIV_NEXT (loop, m); mpfr_set_prec (s, m); mpfr_set_prec (u, m); mpfr_set_prec (v, m); mpfr_set_prec (xx, m); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (y, s, rnd_mode); mpfr_clears (s, u, v, xx, (mpfr_ptr) 0); MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (y, inexact, rnd_mode); } else if (mpfr_cmp_ui_2exp (x, 1, -1) > 0) /* 1/2 < x < 1 */ /* 1 > x > 1/2: Li2(x) = -S(-log(x))+log^2(x)/4-log(x)log(1-x)+pi^2/6 */ { int k; mpfr_t s, u, v, xx; mpfr_init2 (s, m); mpfr_init2 (u, m); mpfr_init2 (v, m); mpfr_init2 (xx, m); MPFR_ZIV_INIT (loop, m); for (;;) { mpfr_log (u, x, GMP_RNDD); mpfr_neg (u, u, GMP_RNDN); k = li2_series (s, u, GMP_RNDN); mpfr_neg (s, s, GMP_RNDN); err = 1 + MPFR_INT_CEIL_LOG2 (k + 1) - MPFR_GET_EXP (s); mpfr_ui_sub (xx, 1, x, GMP_RNDN); mpfr_log (v, xx, GMP_RNDU); mpfr_mul (v, v, u, GMP_RNDN); /* v = - log(x) * log(1-x) */ mpfr_add (s, s, v, GMP_RNDN); err = MAX (err, 1 - MPFR_GET_EXP (v)); err = 2 + MAX (3, err) - MPFR_GET_EXP (s); mpfr_sqr (u, u, GMP_RNDN); mpfr_div_2ui (u, u, 2, GMP_RNDN); /* u = log^2(x)/4 */ mpfr_add (s, s, u, GMP_RNDN); err = MAX (err, 2 + MPFR_GET_EXP (u)) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err) + MPFR_GET_EXP (s); mpfr_const_pi (u, GMP_RNDU); mpfr_sqr (u, u, GMP_RNDN); mpfr_div_ui (u, u, 6, GMP_RNDN); /* u = pi^2/6 */ mpfr_add (s, s, u, GMP_RNDN); err = MAX (err, 3) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err); if (MPFR_CAN_ROUND (s, (mp_exp_t) m - err, yp, rnd_mode)) break; MPFR_ZIV_NEXT (loop, m); mpfr_set_prec (s, m); mpfr_set_prec (u, m); mpfr_set_prec (v, m); mpfr_set_prec (xx, m); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (y, s, rnd_mode); mpfr_clears (s, u, v, xx, (mpfr_ptr) 0); MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (y, inexact, rnd_mode); } else if (mpfr_cmp_si (x, -1) >= 0) /* 0 > x >= -1: Li2(x) = -S(log(1-x))-log^2(1-x)/4 */ { int k; mp_exp_t expo_l; mpfr_t s, u, xx; mpfr_init2 (s, m); mpfr_init2 (u, m); mpfr_init2 (xx, m); MPFR_ZIV_INIT (loop, m); for (;;) { mpfr_neg (xx, x, GMP_RNDN); mpfr_log1p (u, xx, GMP_RNDN); k = li2_series (s, u, GMP_RNDN); mpfr_neg (s, s, GMP_RNDN); expo_l = MPFR_GET_EXP (u); err = 1 + MPFR_INT_CEIL_LOG2 (k + 1) - MPFR_GET_EXP (s); mpfr_sqr (u, u, GMP_RNDN); mpfr_div_2ui (u, u, 2, GMP_RNDN); /* u = log^2(1-x)/4 */ mpfr_sub (s, s, u, GMP_RNDN); err = MAX (err, - expo_l); err = 2 + MAX (err, 3); if (MPFR_CAN_ROUND (s, (mp_exp_t) m - err, yp, rnd_mode)) break; MPFR_ZIV_NEXT (loop, m); mpfr_set_prec (s, m); mpfr_set_prec (u, m); mpfr_set_prec (xx, m); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (y, s, rnd_mode); mpfr_clears (s, u, xx, (mpfr_ptr) 0); MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (y, inexact, rnd_mode); } else /* x < -1: Li2(x) = S(log(1-1/x))-log^2(-x)/4-log(1-x)log(-x)/2+log^2(1-x)/4-pi^2/6 */ { int k; mpfr_t s, u, v, w, xx; if (mpfr_cmp_si (x, -7) <= 0) { inexact = mpfr_li2_asympt_neg (y, x, rnd_mode); if (inexact != 0) goto end_of_case_ltm1; } mpfr_init2 (s, m); mpfr_init2 (u, m); mpfr_init2 (v, m); mpfr_init2 (w, m); mpfr_init2 (xx, m); MPFR_ZIV_INIT (loop, m); for (;;) { mpfr_ui_div (xx, 1, x, GMP_RNDN); mpfr_neg (xx, xx, GMP_RNDN); mpfr_log1p (u, xx, GMP_RNDN); k = li2_series (s, u, GMP_RNDN); mpfr_ui_sub (xx, 1, x, GMP_RNDN); mpfr_log (u, xx, GMP_RNDU); mpfr_neg (xx, x, GMP_RNDN); mpfr_log (v, xx, GMP_RNDU); mpfr_mul (w, v, u, GMP_RNDN); mpfr_div_2ui (w, w, 1, GMP_RNDN); /* w = log(-x) * log(1-x) / 2 */ mpfr_sub (s, s, w, GMP_RNDN); err = 1 + MAX (3, MPFR_INT_CEIL_LOG2 (k+1) + 1 - MPFR_GET_EXP (s)) + MPFR_GET_EXP (s); mpfr_sqr (w, v, GMP_RNDN); mpfr_div_2ui (w, w, 2, GMP_RNDN); /* w = log^2(-x) / 4 */ mpfr_sub (s, s, w, GMP_RNDN); err = MAX (err, 3 + MPFR_GET_EXP(w)) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err) + MPFR_GET_EXP (s); mpfr_sqr (w, u, GMP_RNDN); mpfr_div_2ui (w, w, 2, GMP_RNDN); /* w = log^2(1-x) / 4 */ mpfr_add (s, s, w, GMP_RNDN); err = MAX (err, 3 + MPFR_GET_EXP (w)) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err) + MPFR_GET_EXP (s); mpfr_const_pi (w, GMP_RNDU); mpfr_sqr (w, w, GMP_RNDN); mpfr_div_ui (w, w, 6, GMP_RNDN); /* w = pi^2 / 6 */ mpfr_sub (s, s, w, GMP_RNDN); err = MAX (err, 3) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err) + MPFR_GET_EXP (s); if (MPFR_CAN_ROUND (s, (mp_exp_t) m - err, yp, rnd_mode)) break; MPFR_ZIV_NEXT (loop, m); mpfr_set_prec (s, m); mpfr_set_prec (u, m); mpfr_set_prec (v, m); mpfr_set_prec (w, m); mpfr_set_prec (xx, m); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (y, s, rnd_mode); mpfr_clears (s, u, v, w, xx, (mpfr_ptr) 0); end_of_case_ltm1: MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (y, inexact, rnd_mode); } MPFR_ASSERTN (0); /* should never reach this point */ }
/* Compute the alternating series s = S(z) = \sum_{k=0}^infty B_{2k} (z))^{2k+1} / (2k+1)! with 0 < z <= log(2) to the precision of s rounded in the direction rnd_mode. Return the maximum index of the truncature which is useful for determinating the relative error. */ static int li2_series (mpfr_t sum, mpfr_srcptr z, mpfr_rnd_t rnd_mode) { int i, Bm, Bmax; mpfr_t s, u, v, w; mpfr_prec_t sump, p; mp_exp_t se, err; mpz_t *B; MPFR_ZIV_DECL (loop); /* The series converges for |z| < 2 pi, but in mpfr_li2 the argument is reduced so that 0 < z <= log(2). Here is additionnal check that z is (nearly) correct */ MPFR_ASSERTD (MPFR_IS_STRICTPOS (z)); MPFR_ASSERTD (mpfr_cmp_d (z, 0.6953125) <= 0); sump = MPFR_PREC (sum); /* target precision */ p = sump + MPFR_INT_CEIL_LOG2 (sump) + 4; /* the working precision */ mpfr_init2 (s, p); mpfr_init2 (u, p); mpfr_init2 (v, p); mpfr_init2 (w, p); B = bernoulli ((mpz_t *) 0, 0); Bm = Bmax = 1; MPFR_ZIV_INIT (loop, p); for (;;) { mpfr_sqr (u, z, GMP_RNDU); mpfr_set (v, z, GMP_RNDU); mpfr_set (s, z, GMP_RNDU); se = MPFR_GET_EXP (s); err = 0; for (i = 1;; i++) { if (i >= Bmax) B = bernoulli (B, Bmax++); /* B_2i * (2i+1)!, exact */ mpfr_mul (v, u, v, GMP_RNDU); mpfr_div_ui (v, v, 2 * i, GMP_RNDU); mpfr_div_ui (v, v, 2 * i, GMP_RNDU); mpfr_div_ui (v, v, 2 * i + 1, GMP_RNDU); mpfr_div_ui (v, v, 2 * i + 1, GMP_RNDU); /* here, v_2i = v_{2i-2} / (2i * (2i+1))^2 */ mpfr_mul_z (w, v, B[i], GMP_RNDN); /* here, w_2i = v_2i * B_2i * (2i+1)! with error(w_2i) < 2^(5 * i + 8) ulp(w_2i) (see algorithms.tex) */ mpfr_add (s, s, w, GMP_RNDN); err = MAX (err + se, 5 * i + 8 + MPFR_GET_EXP (w)) - MPFR_GET_EXP (s); err = 2 + MAX (-1, err); se = MPFR_GET_EXP (s); if (MPFR_GET_EXP (w) <= se - (mp_exp_t) p) break; } /* the previous value of err is the rounding error, the truncation error is less than EXP(z) - 6 * i - 5 (see algorithms.tex) */ err = MAX (err, MPFR_GET_EXP (z) - 6 * i - 5) + 1; if (MPFR_CAN_ROUND (s, (mp_exp_t) p - err, sump, rnd_mode)) break; MPFR_ZIV_NEXT (loop, p); mpfr_set_prec (s, p); mpfr_set_prec (u, p); mpfr_set_prec (v, p); mpfr_set_prec (w, p); } MPFR_ZIV_FREE (loop); mpfr_set (sum, s, rnd_mode); Bm = Bmax; while (Bm--) mpz_clear (B[Bm]); (*__gmp_free_func) (B, Bmax * sizeof (mpz_t)); mpfr_clears (s, u, v, w, (mpfr_ptr) 0); /* Let K be the returned value. 1. As we compute an alternating series, the truncation error has the same sign as the next term w_{K+2} which is positive iff K%4 == 0. 2. Assume that error(z) <= (1+t) z', where z' is the actual value, then error(s) <= 2 * (K+1) * t (see algorithms.tex). */ return 2 * i; }
/* Compare the result (z1,inex1) of mpfr_pow with all flags cleared with those of mpfr_pow with all flags set and of the other power functions. Arguments x and y are the input values; sx and sy are their string representations (sx may be null); rnd contains the rounding mode; s is a string containing the function that called test_others. */ static void test_others (const void *sx, const char *sy, mpfr_rnd_t rnd, mpfr_srcptr x, mpfr_srcptr y, mpfr_srcptr z1, int inex1, unsigned int flags, const char *s) { mpfr_t z2; int inex2; int spx = sx != NULL; if (!spx) sx = x; mpfr_init2 (z2, mpfr_get_prec (z1)); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_pow (z2, x, y, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_pow, flags set"); /* If y is an integer that fits in an unsigned long and is not -0, we can test mpfr_pow_ui. */ if (MPFR_IS_POS (y) && mpfr_integer_p (y) && mpfr_fits_ulong_p (y, MPFR_RNDN)) { unsigned long yy = mpfr_get_ui (y, MPFR_RNDN); mpfr_clear_flags (); inex2 = mpfr_pow_ui (z2, x, yy, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_pow_ui, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_pow_ui (z2, x, yy, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_pow_ui, flags set"); /* If x is an integer that fits in an unsigned long and is not -0, we can also test mpfr_ui_pow_ui. */ if (MPFR_IS_POS (x) && mpfr_integer_p (x) && mpfr_fits_ulong_p (x, MPFR_RNDN)) { unsigned long xx = mpfr_get_ui (x, MPFR_RNDN); mpfr_clear_flags (); inex2 = mpfr_ui_pow_ui (z2, xx, yy, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_ui_pow_ui, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_ui_pow_ui (z2, xx, yy, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_ui_pow_ui, flags set"); } } /* If y is an integer but not -0 and not huge, we can test mpfr_pow_z, and possibly mpfr_pow_si (and possibly mpfr_ui_div). */ if (MPFR_IS_ZERO (y) ? MPFR_IS_POS (y) : (mpfr_integer_p (y) && MPFR_GET_EXP (y) < 256)) { mpz_t yyy; /* If y fits in a long, we can test mpfr_pow_si. */ if (mpfr_fits_slong_p (y, MPFR_RNDN)) { long yy = mpfr_get_si (y, MPFR_RNDN); mpfr_clear_flags (); inex2 = mpfr_pow_si (z2, x, yy, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_pow_si, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_pow_si (z2, x, yy, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_pow_si, flags set"); /* If y = -1, we can test mpfr_ui_div. */ if (yy == -1) { mpfr_clear_flags (); inex2 = mpfr_ui_div (z2, 1, x, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_ui_div, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_ui_div (z2, 1, x, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_ui_div, flags set"); } /* If y = 2, we can test mpfr_sqr. */ if (yy == 2) { mpfr_clear_flags (); inex2 = mpfr_sqr (z2, x, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_sqr, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_sqr (z2, x, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_sqr, flags set"); } } /* Test mpfr_pow_z. */ mpz_init (yyy); mpfr_get_z (yyy, y, MPFR_RNDN); mpfr_clear_flags (); inex2 = mpfr_pow_z (z2, x, yyy, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_pow_z, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_pow_z (z2, x, yyy, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_pow_z, flags set"); mpz_clear (yyy); } /* If y = 0.5, we can test mpfr_sqrt, except if x is -0 or -Inf (because the rule for mpfr_pow on these special values is different). */ if (MPFR_IS_PURE_FP (y) && mpfr_cmp_str1 (y, "0.5") == 0 && ! ((MPFR_IS_ZERO (x) || MPFR_IS_INF (x)) && MPFR_IS_NEG (x))) { mpfr_clear_flags (); inex2 = mpfr_sqrt (z2, x, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_sqrt, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_sqrt (z2, x, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_sqrt, flags set"); } #if MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0) /* If y = -0.5, we can test mpfr_rec_sqrt, except if x = -Inf (because the rule for mpfr_pow on -Inf is different). */ if (MPFR_IS_PURE_FP (y) && mpfr_cmp_str1 (y, "-0.5") == 0 && ! (MPFR_IS_INF (x) && MPFR_IS_NEG (x))) { mpfr_clear_flags (); inex2 = mpfr_rec_sqrt (z2, x, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_rec_sqrt, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_rec_sqrt (z2, x, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_rec_sqrt, flags set"); } #endif /* If x is an integer that fits in an unsigned long and is not -0, we can test mpfr_ui_pow. */ if (MPFR_IS_POS (x) && mpfr_integer_p (x) && mpfr_fits_ulong_p (x, MPFR_RNDN)) { unsigned long xx = mpfr_get_ui (x, MPFR_RNDN); mpfr_clear_flags (); inex2 = mpfr_ui_pow (z2, xx, y, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_ui_pow, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_ui_pow (z2, xx, y, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_ui_pow, flags set"); /* If x = 2, we can test mpfr_exp2. */ if (xx == 2) { mpfr_clear_flags (); inex2 = mpfr_exp2 (z2, y, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_exp2, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_exp2 (z2, y, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_exp2, flags set"); } /* If x = 10, we can test mpfr_exp10. */ if (xx == 10) { mpfr_clear_flags (); inex2 = mpfr_exp10 (z2, y, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags, s, "mpfr_exp10, flags cleared"); __gmpfr_flags = MPFR_FLAGS_ALL; inex2 = mpfr_exp10 (z2, y, rnd); cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL, s, "mpfr_exp10, flags set"); } } mpfr_clear (z2); }
int mpc_atan (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) { int s_re; int s_im; int inex_re; int inex_im; int inex; inex_re = 0; inex_im = 0; s_re = mpfr_signbit (mpc_realref (op)); s_im = mpfr_signbit (mpc_imagref (op)); /* special values */ if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op))) { if (mpfr_nan_p (mpc_realref (op))) { mpfr_set_nan (mpc_realref (rop)); if (mpfr_zero_p (mpc_imagref (op)) || mpfr_inf_p (mpc_imagref (op))) { mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN); if (s_im) mpc_conj (rop, rop, MPC_RNDNN); } else mpfr_set_nan (mpc_imagref (rop)); } else { if (mpfr_inf_p (mpc_realref (op))) { inex_re = set_pi_over_2 (mpc_realref (rop), -s_re, MPC_RND_RE (rnd)); mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN); } else { mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); } } return MPC_INEX (inex_re, 0); } if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op))) { inex_re = set_pi_over_2 (mpc_realref (rop), -s_re, MPC_RND_RE (rnd)); mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN); if (s_im) mpc_conj (rop, rop, GMP_RNDN); return MPC_INEX (inex_re, 0); } /* pure real argument */ if (mpfr_zero_p (mpc_imagref (op))) { inex_re = mpfr_atan (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN); if (s_im) mpc_conj (rop, rop, GMP_RNDN); return MPC_INEX (inex_re, 0); } /* pure imaginary argument */ if (mpfr_zero_p (mpc_realref (op))) { int cmp_1; if (s_im) cmp_1 = -mpfr_cmp_si (mpc_imagref (op), -1); else cmp_1 = mpfr_cmp_ui (mpc_imagref (op), +1); if (cmp_1 < 0) { /* atan(+0+iy) = +0 +i*atanh(y), if |y| < 1 atan(-0+iy) = -0 +i*atanh(y), if |y| < 1 */ mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN); if (s_re) mpfr_neg (mpc_realref (rop), mpc_realref (rop), GMP_RNDN); inex_im = mpfr_atanh (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd)); } else if (cmp_1 == 0) { /* atan(+/-0+i) = NaN +i*inf atan(+/-0-i) = NaN -i*inf */ mpfr_set_nan (mpc_realref (rop)); mpfr_set_inf (mpc_imagref (rop), s_im ? -1 : +1); } else { /* atan(+0+iy) = +pi/2 +i*atanh(1/y), if |y| > 1 atan(-0+iy) = -pi/2 +i*atanh(1/y), if |y| > 1 */ mpfr_rnd_t rnd_im, rnd_away; mpfr_t y; mpfr_prec_t p, p_im; int ok; rnd_im = MPC_RND_IM (rnd); mpfr_init (y); p_im = mpfr_get_prec (mpc_imagref (rop)); p = p_im; /* a = o(1/y) with error(a) < 1 ulp(a) b = o(atanh(a)) with error(b) < (1+2^{1+Exp(a)-Exp(b)}) ulp(b) As |atanh (1/y)| > |1/y| we have Exp(a)-Exp(b) <=0 so, at most, 2 bits of precision are lost. We round atanh(1/y) away from 0. */ do { p += mpc_ceil_log2 (p) + 2; mpfr_set_prec (y, p); rnd_away = s_im == 0 ? GMP_RNDU : GMP_RNDD; inex_im = mpfr_ui_div (y, 1, mpc_imagref (op), rnd_away); /* FIXME: should we consider the case with unreasonably huge precision prec(y)>3*exp_min, where atanh(1/Im(op)) could be representable while 1/Im(op) underflows ? This corresponds to |y| = 0.5*2^emin, in which case the result may be wrong. */ /* atanh cannot underflow: |atanh(x)| > |x| for |x| < 1 */ inex_im |= mpfr_atanh (y, y, rnd_away); ok = inex_im == 0 || mpfr_can_round (y, p - 2, rnd_away, GMP_RNDZ, p_im + (rnd_im == GMP_RNDN)); } while (ok == 0); inex_re = set_pi_over_2 (mpc_realref (rop), -s_re, MPC_RND_RE (rnd)); inex_im = mpfr_set (mpc_imagref (rop), y, rnd_im); mpfr_clear (y); } return MPC_INEX (inex_re, inex_im); } /* regular number argument */ { mpfr_t a, b, x, y; mpfr_prec_t prec, p; mpfr_exp_t err, expo; int ok = 0; mpfr_t minus_op_re; mpfr_exp_t op_re_exp, op_im_exp; mpfr_rnd_t rnd1, rnd2; mpfr_inits2 (MPFR_PREC_MIN, a, b, x, y, (mpfr_ptr) 0); /* real part: Re(arctan(x+i*y)) = [arctan2(x,1-y) - arctan2(-x,1+y)]/2 */ minus_op_re[0] = mpc_realref (op)[0]; MPFR_CHANGE_SIGN (minus_op_re); op_re_exp = mpfr_get_exp (mpc_realref (op)); op_im_exp = mpfr_get_exp (mpc_imagref (op)); prec = mpfr_get_prec (mpc_realref (rop)); /* result precision */ /* a = o(1-y) error(a) < 1 ulp(a) b = o(atan2(x,a)) error(b) < [1+2^{3+Exp(x)-Exp(a)-Exp(b)}] ulp(b) = kb ulp(b) c = o(1+y) error(c) < 1 ulp(c) d = o(atan2(-x,c)) error(d) < [1+2^{3+Exp(x)-Exp(c)-Exp(d)}] ulp(d) = kd ulp(d) e = o(b - d) error(e) < [1 + kb*2^{Exp(b}-Exp(e)} + kd*2^{Exp(d)-Exp(e)}] ulp(e) error(e) < [1 + 2^{4+Exp(x)-Exp(a)-Exp(e)} + 2^{4+Exp(x)-Exp(c)-Exp(e)}] ulp(e) because |atan(u)| < |u| < [1 + 2^{5+Exp(x)-min(Exp(a),Exp(c)) -Exp(e)}] ulp(e) f = e/2 exact */ /* p: working precision */ p = (op_im_exp > 0 || prec > SAFE_ABS (mpfr_prec_t, op_im_exp)) ? prec : (prec - op_im_exp); rnd1 = mpfr_sgn (mpc_realref (op)) > 0 ? GMP_RNDD : GMP_RNDU; rnd2 = mpfr_sgn (mpc_realref (op)) < 0 ? GMP_RNDU : GMP_RNDD; do { p += mpc_ceil_log2 (p) + 2; mpfr_set_prec (a, p); mpfr_set_prec (b, p); mpfr_set_prec (x, p); /* x = upper bound for atan (x/(1-y)). Since atan is increasing, we need an upper bound on x/(1-y), i.e., a lower bound on 1-y for x positive, and an upper bound on 1-y for x negative */ mpfr_ui_sub (a, 1, mpc_imagref (op), rnd1); if (mpfr_sgn (a) == 0) /* y is near 1, thus 1+y is near 2, and expo will be 1 or 2 below */ { MPC_ASSERT (mpfr_cmp_ui (mpc_imagref(op), 1) == 0); /* check for intermediate underflow */ err = 2; /* ensures err will be expo below */ } else err = mpfr_get_exp (a); /* err = Exp(a) with the notations above */ mpfr_atan2 (x, mpc_realref (op), a, GMP_RNDU); /* b = lower bound for atan (-x/(1+y)): for x negative, we need a lower bound on -x/(1+y), i.e., an upper bound on 1+y */ mpfr_add_ui (a, mpc_imagref(op), 1, rnd2); /* if a is exactly zero, i.e., Im(op) = -1, then the error on a is 0, and we can simply ignore the terms involving Exp(a) in the error */ if (mpfr_sgn (a) == 0) { MPC_ASSERT (mpfr_cmp_si (mpc_imagref(op), -1) == 0); /* check for intermediate underflow */ expo = err; /* will leave err unchanged below */ } else expo = mpfr_get_exp (a); /* expo = Exp(c) with the notations above */ mpfr_atan2 (b, minus_op_re, a, GMP_RNDD); err = err < expo ? err : expo; /* err = min(Exp(a),Exp(c)) */ mpfr_sub (x, x, b, GMP_RNDU); err = 5 + op_re_exp - err - mpfr_get_exp (x); /* error is bounded by [1 + 2^err] ulp(e) */ err = err < 0 ? 1 : err + 1; mpfr_div_2ui (x, x, 1, GMP_RNDU); /* Note: using RND2=RNDD guarantees that if x is exactly representable on prec + ... bits, mpfr_can_round will return 0 */ ok = mpfr_can_round (x, p - err, GMP_RNDU, GMP_RNDD, prec + (MPC_RND_RE (rnd) == GMP_RNDN)); } while (ok == 0); /* Imaginary part Im(atan(x+I*y)) = 1/4 * [log(x^2+(1+y)^2) - log (x^2 +(1-y)^2)] */ prec = mpfr_get_prec (mpc_imagref (rop)); /* result precision */ /* a = o(1+y) error(a) < 1 ulp(a) b = o(a^2) error(b) < 5 ulp(b) c = o(x^2) error(c) < 1 ulp(c) d = o(b+c) error(d) < 7 ulp(d) e = o(log(d)) error(e) < [1 + 7*2^{2-Exp(e)}] ulp(e) = ke ulp(e) f = o(1-y) error(f) < 1 ulp(f) g = o(f^2) error(g) < 5 ulp(g) h = o(c+f) error(h) < 7 ulp(h) i = o(log(h)) error(i) < [1 + 7*2^{2-Exp(i)}] ulp(i) = ki ulp(i) j = o(e-i) error(j) < [1 + ke*2^{Exp(e)-Exp(j)} + ki*2^{Exp(i)-Exp(j)}] ulp(j) error(j) < [1 + 2^{Exp(e)-Exp(j)} + 2^{Exp(i)-Exp(j)} + 7*2^{3-Exp(j)}] ulp(j) < [1 + 2^{max(Exp(e),Exp(i))-Exp(j)+1} + 7*2^{3-Exp(j)}] ulp(j) k = j/4 exact */ err = 2; p = prec; /* working precision */ do { p += mpc_ceil_log2 (p) + err; mpfr_set_prec (a, p); mpfr_set_prec (b, p); mpfr_set_prec (y, p); /* a = upper bound for log(x^2 + (1+y)^2) */ ROUND_AWAY (mpfr_add_ui (a, mpc_imagref (op), 1, MPFR_RNDA), a); mpfr_sqr (a, a, GMP_RNDU); mpfr_sqr (y, mpc_realref (op), GMP_RNDU); mpfr_add (a, a, y, GMP_RNDU); mpfr_log (a, a, GMP_RNDU); /* b = lower bound for log(x^2 + (1-y)^2) */ mpfr_ui_sub (b, 1, mpc_imagref (op), GMP_RNDZ); /* round to zero */ mpfr_sqr (b, b, GMP_RNDZ); /* we could write mpfr_sqr (y, mpc_realref (op), GMP_RNDZ) but it is more efficient to reuse the value of y (x^2) above and subtract one ulp */ mpfr_nextbelow (y); mpfr_add (b, b, y, GMP_RNDZ); mpfr_log (b, b, GMP_RNDZ); mpfr_sub (y, a, b, GMP_RNDU); if (mpfr_zero_p (y)) /* FIXME: happens when x and y have very different magnitudes; could be handled more efficiently */ ok = 0; else { expo = MPC_MAX (mpfr_get_exp (a), mpfr_get_exp (b)); expo = expo - mpfr_get_exp (y) + 1; err = 3 - mpfr_get_exp (y); /* error(j) <= [1 + 2^expo + 7*2^err] ulp(j) */ if (expo <= err) /* error(j) <= [1 + 2^{err+1}] ulp(j) */ err = (err < 0) ? 1 : err + 2; else err = (expo < 0) ? 1 : expo + 2; mpfr_div_2ui (y, y, 2, GMP_RNDN); MPC_ASSERT (!mpfr_zero_p (y)); /* FIXME: underflow. Since the main term of the Taylor series in y=0 is 1/(x^2+1) * y, this means that y is very small and/or x very large; but then the mpfr_zero_p (y) above should be true. This needs a proof, or better yet, special code. */ ok = mpfr_can_round (y, p - err, GMP_RNDU, GMP_RNDD, prec + (MPC_RND_IM (rnd) == GMP_RNDN)); } } while (ok == 0); inex = mpc_set_fr_fr (rop, x, y, rnd); mpfr_clears (a, b, x, y, (mpfr_ptr) 0); return inex; } }