/* bug found in nightly tests */ static void test20071214 (void) { mpfr_t a, b; int inex; mpfr_init2 (a, 4); mpfr_init2 (b, 4); mpfr_set_ui_2exp (a, 3, -4, MPFR_RNDN); inex = mpfr_sin_cos (a, b, a, MPFR_RNDD); MPFR_ASSERTN(mpfr_cmp_ui_2exp (a, 11, -6) == 0); MPFR_ASSERTN(mpfr_cmp_ui_2exp (b, 15, -4) == 0); MPFR_ASSERTN(inex == 10); mpfr_set_ui_2exp (a, 3, -4, MPFR_RNDN); inex = mpfr_sin_cos (a, b, a, MPFR_RNDU); MPFR_ASSERTN(mpfr_cmp_ui_2exp (a, 3, -4) == 0); MPFR_ASSERTN(mpfr_cmp_ui (b, 1) == 0); MPFR_ASSERTN(inex == 5); mpfr_set_ui_2exp (a, 3, -4, MPFR_RNDN); inex = mpfr_sin_cos (a, b, a, MPFR_RNDN); MPFR_ASSERTN(mpfr_cmp_ui_2exp (a, 3, -4) == 0); MPFR_ASSERTN(mpfr_cmp_ui (b, 1) == 0); MPFR_ASSERTN(inex == 5); mpfr_clear (a); mpfr_clear (b); }
static void check53 (const char *xs, const char *sin_xs, const char *cos_xs, mpfr_rnd_t rnd_mode) { mpfr_t xx, s, c; mpfr_inits2 (53, xx, s, c, (mpfr_ptr) 0); mpfr_set_str1 (xx, xs); /* should be exact */ mpfr_sin_cos (s, c, xx, rnd_mode); if (mpfr_cmp_str1 (s, sin_xs)) { printf ("mpfr_sin_cos failed for x=%s, rnd=%s\n", xs, mpfr_print_rnd_mode (rnd_mode)); printf ("mpfr_sin_cos gives sin(x)="); mpfr_out_str(stdout, 10, 0, s, MPFR_RNDN); printf(", expected %s\n", sin_xs); exit (1); } if (mpfr_cmp_str1 (c, cos_xs)) { printf ("mpfr_sin_cos failed for x=%s, rnd=%s\n", xs, mpfr_print_rnd_mode (rnd_mode)); printf ("mpfr_sin_cos gives cos(x)="); mpfr_out_str(stdout, 10, 0, c, MPFR_RNDN); printf(", expected %s\n", cos_xs); exit (1); } mpfr_clears (xx, s, c, (mpfr_ptr) 0); }
static void coverage_01032011 (void) { mpfr_t val, cval, sval, svalf; int status_f, status; mpfr_init2 (val, MPFR_PREC_MIN); mpfr_init2 (cval, MPFR_PREC_MIN); mpfr_init2 (sval, MPFR_PREC_MIN); mpfr_init2 (svalf, MPFR_PREC_MIN); mpfr_set_str1 (val, "-0.7"); status_f = mpfr_sincos_fast (svalf, NULL, val, MPFR_RNDN); status = mpfr_sin_cos (sval, cval, val, MPFR_RNDN); if (! mpfr_equal_p (svalf, sval) || SIGN (status_f) != SIGN (status)) { printf ("mpfr_sincos_fast differ from mpfr_sin_cos result:\n" " sin fast is "); mpfr_dump (svalf); printf (" sin is "); mpfr_dump (sval); printf ("status_f = %d, status = %d\n", status_f, status); exit (1); } mpfr_clears(val, cval, sval, svalf, (mpfr_ptr) 0); }
/* check that mpfr_sin_cos and test_mpfr_sincos_fast agree */ static void test_mpfr_sincos_fast (void) { mpfr_t x, y, z, yref, zref, h; mpfr_prec_t p = 1000; int i, inex, inexref; mpfr_rnd_t r; mpfr_init2 (x, p); mpfr_init2 (y, p); mpfr_init2 (z, p); mpfr_init2 (yref, p); mpfr_init2 (zref, p); mpfr_init2 (h, p); mpfr_set_ui (x, 0, MPFR_RNDN); /* we generate a random value x, compute sin(x) and cos(x) with both mpfr_sin_cos and mpfr_sincos_fast, and check the values and the flags agree */ for (i = 0; i < 100; i++) { mpfr_urandomb (h, RANDS); mpfr_add (x, x, h, MPFR_RNDN); r = RND_RAND (); inexref = mpfr_sin_cos (yref, zref, x, r); inex = mpfr_sincos_fast (y, z, x, r); if (mpfr_cmp (y, yref)) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree\n"); printf ("x="); mpfr_dump (x); printf ("rnd=%s\n", mpfr_print_rnd_mode (r)); printf ("yref="); mpfr_dump (yref); printf ("y="); mpfr_dump (y); exit (1); } if (mpfr_cmp (z, zref)) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree\n"); printf ("x="); mpfr_dump (x); printf ("rnd=%s\n", mpfr_print_rnd_mode (r)); printf ("zref="); mpfr_dump (zref); printf ("z="); mpfr_dump (z); exit (1); } if (inex != inexref) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree\n"); printf ("x="); mpfr_dump (x); printf ("rnd=%s\n", mpfr_print_rnd_mode (r)); printf ("inexref=%d inex=%d\n", inexref, inex); exit (1); } } mpfr_clear (x); mpfr_clear (y); mpfr_clear (z); mpfr_clear (yref); mpfr_clear (zref); mpfr_clear (h); }
/* Note: with the sin_cos.c code before r6507, the disagreement occurs only on the return ("inexact") value, which is new in r6444. */ static void bug20091008 (void) { mpfr_t x, y, z, yref, zref; mpfr_prec_t p = 1000; int inex, inexref; mpfr_rnd_t r = MPFR_RNDN; mpfr_init2 (x, p); mpfr_init2 (y, p); mpfr_init2 (z, p); mpfr_init2 (yref, p); mpfr_init2 (zref, p); mpfr_set_str (x, "c.91813724e28ef6a711d33e6505984699daef7fe93636c1ed5d0168bc96989cc6802f7f9e405c902ec62fb90cd39c9d21084c8ad8b5af4c4aa87bf402e2e4a78e6fe1ffeb6dbbbdbbc2983c196c518966ccc1e094ed39ee77984ef2428069d65de37928e75247edbe7007245e682616b5ebbf05f2fdefc74ad192024f10", 16, MPFR_RNDN); inexref = mpfr_sin_cos (yref, zref, x, r); inex = mpfr_sincos_fast (y, z, x, r); if (mpfr_cmp (y, yref)) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree (bug20091008)\n"); printf ("yref="); mpfr_dump (yref); printf ("y="); mpfr_dump (y); exit (1); } if (mpfr_cmp (z, zref)) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree (bug20091008)\n"); printf ("zref="); mpfr_dump (zref); printf ("z="); mpfr_dump (z); exit (1); } /* sin(x) is rounded up, cos(x) is rounded up too, thus we should get 5 for the return value */ if (inex != inexref) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree (bug20091008)\n"); printf ("inexref=%d inex=%d (5 expected)\n", inexref, inex); exit (1); } mpfr_clear (x); mpfr_clear (y); mpfr_clear (z); mpfr_clear (yref); mpfr_clear (zref); }
static void bug20091013 (void) { mpfr_t x, y, z, yref, zref; mpfr_prec_t p = 1000; int inex, inexref; mpfr_rnd_t r = MPFR_RNDN; mpfr_init2 (x, p); mpfr_init2 (y, p); mpfr_init2 (z, p); mpfr_init2 (yref, p); mpfr_init2 (zref, p); mpfr_set_str (x, "3.240ff3fdcb1ee7cd667b96287593ae24e20fb63ed7c2d5bf4bd0f2cc5509283b04e7628e66382605f14ed5967cef15296041539a1bdaa626c777c7fbb6f2068414759b78cee14f37848689b3a170f583656be4e0837f464d8210556a3a822d4ecfdd59f4e0d5fdb76bf7e15b8a57234e2160b98e14c17bbdf27c4643b8@1", 16, MPFR_RNDN); inexref = mpfr_sin_cos (yref, zref, x, r); inex = mpfr_sincos_fast (y, z, x, r); if (mpfr_cmp (y, yref)) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree (bug20091013)\n"); printf ("yref="); mpfr_dump (yref); printf ("y="); mpfr_dump (y); exit (1); } if (mpfr_cmp (z, zref)) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree (bug20091013)\n"); printf ("zref="); mpfr_dump (zref); printf ("z="); mpfr_dump (z); exit (1); } /* sin(x) is rounded down and cos(x) is rounded down, thus we should get 2+4*2 = 10 as return value */ if (inex != inexref) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree (bug20091013)\n"); printf ("inexref=%d inex=%d (10 expected)\n", inexref, inex); exit (1); } mpfr_clear (x); mpfr_clear (y); mpfr_clear (z); mpfr_clear (yref); mpfr_clear (zref); }
static void bug20091007 (void) { mpfr_t x, y, z, yref, zref; mpfr_prec_t p = 1000; int inex, inexref; mpfr_rnd_t r = MPFR_RNDZ; mpfr_init2 (x, p); mpfr_init2 (y, p); mpfr_init2 (z, p); mpfr_init2 (yref, p); mpfr_init2 (zref, p); mpfr_set_str (x, "1.9ecdc22ba77a5ab2560f7e84289e2a328906f47377ea3fd4c82d1bb2f13ee05c032cffc1933eadab7b0a5498e03e3bd0508968e59c25829d97a0b54f20cd4662c8dfffa54e714de41fc8ee3e0e0b244d110a194db05b70022b7d77f88955d415b09f17dd404576098dc51a583a3e49c35839551646e880c7eb790a01a4@1", 16, MPFR_RNDN); inexref = mpfr_sin_cos (yref, zref, x, r); inex = mpfr_sincos_fast (y, z, x, r); if (mpfr_cmp (y, yref)) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree (bug20091007)\n"); printf ("yref="); mpfr_dump (yref); printf ("y="); mpfr_dump (y); exit (1); } if (mpfr_cmp (z, zref)) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree (bug20091007)\n"); printf ("zref="); mpfr_dump (zref); printf ("z="); mpfr_dump (z); exit (1); } if (inex != inexref) { printf ("mpfr_sin_cos and mpfr_sincos_fast disagree (bug20091007)\n"); printf ("inexref=%d inex=%d\n", inexref, inex); exit (1); } mpfr_clear (x); mpfr_clear (y); mpfr_clear (z); mpfr_clear (yref); mpfr_clear (zref); }
static void large_test (char *X, int prec, int N) { int i; mpfr_t x, s, c; mpfr_init2 (x, prec); mpfr_init2 (s, prec); mpfr_init2 (c, prec); mpfr_set_str (x, X, 10, MPFR_RNDN); for (i = 0; i < N; i++) mpfr_sin_cos (s, c, x, MPFR_RNDN); mpfr_clear (x); mpfr_clear (s); mpfr_clear (c); }
static void check_nans (void) { mpfr_t x, s, c; mpfr_init2 (x, 123L); mpfr_init2 (s, 123L); mpfr_init2 (c, 123L); /* sin(NaN)==NaN, cos(NaN)==NaN */ mpfr_set_nan (x); mpfr_sin_cos (s, c, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_nan_p (s)); MPFR_ASSERTN (mpfr_nan_p (c)); /* sin(+Inf)==NaN, cos(+Inf)==NaN */ mpfr_set_inf (x, 1); mpfr_sin_cos (s, c, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_nan_p (s)); MPFR_ASSERTN (mpfr_nan_p (c)); /* sin(-Inf)==NaN, cos(-Inf)==NaN */ mpfr_set_inf (x, -1); mpfr_sin_cos (s, c, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_nan_p (s)); MPFR_ASSERTN (mpfr_nan_p (c)); /* check zero */ mpfr_set_ui (x, 0, MPFR_RNDN); mpfr_sin_cos (s, c, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_cmp_ui (s, 0) == 0 && MPFR_IS_POS (s)); MPFR_ASSERTN (mpfr_cmp_ui (c, 1) == 0); mpfr_neg (x, x, MPFR_RNDN); mpfr_sin_cos (s, c, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_cmp_ui (s, 0) == 0 && MPFR_IS_NEG (s)); MPFR_ASSERTN (mpfr_cmp_ui (c, 1) == 0); /* coverage test */ mpfr_set_prec (x, 2); mpfr_set_ui (x, 4, MPFR_RNDN); mpfr_set_prec (s, 2); mpfr_set_prec (c, 2); mpfr_sin_cos (s, c, x, MPFR_RNDN); MPFR_ASSERTN (mpfr_cmp_si_2exp (s, -3, -2) == 0); MPFR_ASSERTN (mpfr_cmp_si_2exp (c, -3, -2) == 0); mpfr_clear (x); mpfr_clear (s); mpfr_clear (c); }
static void tiny (void) { mpfr_t x, s, c; int i, inex; mpfr_inits2 (64, x, s, c, (mpfr_ptr) 0); for (i = -1; i <= 1; i += 2) { mpfr_set_si (x, i, MPFR_RNDN); mpfr_set_exp (x, mpfr_get_emin ()); inex = mpfr_sin_cos (s, c, x, MPFR_RNDN); MPFR_ASSERTN (inex != 0); MPFR_ASSERTN (mpfr_equal_p (s, x)); MPFR_ASSERTN (!mpfr_nan_p (c) && mpfr_cmp_ui (c, 1) == 0); } mpfr_clears (x, s, c, (mpfr_ptr) 0); }
static PyObject * _GMPy_MPFR_Sin_Cos(PyObject *x, CTXT_Object *context) { MPFR_Object *s, *c; PyObject *result; int code; CHECK_CONTEXT(context); s = GMPy_MPFR_New(0, context); c = GMPy_MPFR_New(0, context); result = PyTuple_New(2); if (!s || !c || !result) { Py_XDECREF((PyObject*)s); Py_XDECREF((PyObject*)c); Py_XDECREF(result); return NULL; } mpfr_clear_flags(); code = mpfr_sin_cos(s->f, c->f, MPFR(x), GET_MPFR_ROUND(context)); s->rc = code & 0x03; c->rc = code >> 2; if (s->rc == 2) s->rc = -1; if (c->rc == 2) c->rc = -1; _GMPy_MPFR_Cleanup(&s, context); _GMPy_MPFR_Cleanup(&c, context); if (!s || !c) { Py_XDECREF((PyObject*)s); Py_XDECREF((PyObject*)c); Py_DECREF(result); return NULL; } PyTuple_SET_ITEM(result, 0, (PyObject*)s); PyTuple_SET_ITEM(result, 1, (PyObject*)c); return result; }
/* Bug reported by Laurent Fousse for the 2.4 branch. No problem in the trunk. https://sympa.inria.fr/sympa/arc/mpfr/2009-11/msg00044.html */ static void bug20091122 (void) { mpfr_t x, y, z, yref, zref; mpfr_prec_t p = 3; mpfr_rnd_t r = MPFR_RNDN; mpfr_init2 (x, 5); mpfr_init2 (y, p); mpfr_init2 (z, p); mpfr_init2 (yref, p); mpfr_init2 (zref, p); mpfr_set_str (x, "0.11111E49", 2, MPFR_RNDN); mpfr_sin_cos (yref, zref, x, r); mpfr_sin (y, x, r); mpfr_cos (z, x, r); if (! mpfr_equal_p (y, yref)) { printf ("mpfr_sin_cos and mpfr_sin disagree (bug20091122)\n"); printf ("yref = "); mpfr_dump (yref); printf ("y = "); mpfr_dump (y); exit (1); } if (! mpfr_equal_p (z, zref)) { printf ("mpfr_sin_cos and mpfr_cos disagree (bug20091122)\n"); printf ("zref = "); mpfr_dump (zref); printf ("z = "); mpfr_dump (z); exit (1); } mpfr_clear (x); mpfr_clear (y); mpfr_clear (z); mpfr_clear (yref); mpfr_clear (zref); }
static bool do_mpfr_sincos (real_value *result_sin, real_value *result_cos, const real_value *arg, const real_format *format) { /* To proceed, MPFR must exactly represent the target floating point format, which only happens when the target base equals two. */ if (format->b != 2 || !real_isfinite (arg)) return false; int prec = format->p; mp_rnd_t rnd = format->round_towards_zero ? GMP_RNDZ : GMP_RNDN; mpfr_t m, ms, mc; mpfr_inits2 (prec, m, ms, mc, NULL); mpfr_from_real (m, arg, GMP_RNDN); mpfr_clear_flags (); bool inexact = mpfr_sin_cos (ms, mc, m, rnd); bool ok = (do_mpfr_ckconv (result_sin, ms, inexact, format) && do_mpfr_ckconv (result_cos, mc, inexact, format)); mpfr_clears (m, ms, mc, NULL); return ok; }
/* Implements asymptotic expansion for jn or yn (formulae 9.2.5 and 9.2.6 from Abramowitz & Stegun). Assumes |z| > p log(2)/2, where p is the target precision (z can be negative only for jn). Return 0 if the expansion does not converge enough (the value 0 as inexact flag should not happen for normal input). */ static int FUNCTION (mpfr_ptr res, long n, mpfr_srcptr z, mpfr_rnd_t r) { mpfr_t s, c, P, Q, t, iz, err_t, err_s, err_u; mpfr_prec_t w; long k; int inex, stop, diverge = 0; mpfr_exp_t err2, err; MPFR_ZIV_DECL (loop); mpfr_init (c); w = MPFR_PREC(res) + MPFR_INT_CEIL_LOG2(MPFR_PREC(res)) + 4; MPFR_ZIV_INIT (loop, w); for (;;) { mpfr_set_prec (c, w); mpfr_init2 (s, w); mpfr_init2 (P, w); mpfr_init2 (Q, w); mpfr_init2 (t, w); mpfr_init2 (iz, w); mpfr_init2 (err_t, 31); mpfr_init2 (err_s, 31); mpfr_init2 (err_u, 31); /* Approximate sin(z) and cos(z). In the following, err <= k means that the approximate value y and the true value x are related by y = x * (1 + u)^k with |u| <= 2^(-w), following Higham's method. */ mpfr_sin_cos (s, c, z, MPFR_RNDN); if (MPFR_IS_NEG(z)) mpfr_neg (s, s, MPFR_RNDN); /* compute jn/yn(|z|), fix sign later */ /* The absolute error on s/c is bounded by 1/2 ulp(1/2) <= 2^(-w-1). */ mpfr_add (t, s, c, MPFR_RNDN); mpfr_sub (c, s, c, MPFR_RNDN); mpfr_swap (s, t); /* now s approximates sin(z)+cos(z), and c approximates sin(z)-cos(z), with total absolute error bounded by 2^(1-w). */ /* precompute 1/(8|z|) */ mpfr_si_div (iz, MPFR_IS_POS(z) ? 1 : -1, z, MPFR_RNDN); /* err <= 1 */ mpfr_div_2ui (iz, iz, 3, MPFR_RNDN); /* compute P and Q */ mpfr_set_ui (P, 1, MPFR_RNDN); mpfr_set_ui (Q, 0, MPFR_RNDN); mpfr_set_ui (t, 1, MPFR_RNDN); /* current term */ mpfr_set_ui (err_t, 0, MPFR_RNDN); /* error on t */ mpfr_set_ui (err_s, 0, MPFR_RNDN); /* error on P and Q (sum of errors) */ for (k = 1, stop = 0; stop < 4; k++) { /* compute next term: t(k)/t(k-1) = (2n+2k-1)(2n-2k+1)/(8kz) */ mpfr_mul_si (t, t, 2 * (n + k) - 1, MPFR_RNDN); /* err <= err_k + 1 */ mpfr_mul_si (t, t, 2 * (n - k) + 1, MPFR_RNDN); /* err <= err_k + 2 */ mpfr_div_ui (t, t, k, MPFR_RNDN); /* err <= err_k + 3 */ mpfr_mul (t, t, iz, MPFR_RNDN); /* err <= err_k + 5 */ /* the relative error on t is bounded by (1+u)^(5k)-1, which is bounded by 6ku for 6ku <= 0.02: first |5 log(1+u)| <= |5.5u| for |u| <= 0.15, then |exp(5.5u)-1| <= 6u for |u| <= 0.02. */ mpfr_mul_ui (err_t, t, 6 * k, MPFR_IS_POS(t) ? MPFR_RNDU : MPFR_RNDD); mpfr_abs (err_t, err_t, MPFR_RNDN); /* exact */ /* the absolute error on t is bounded by err_t * 2^(-w) */ mpfr_abs (err_u, t, MPFR_RNDU); mpfr_mul_2ui (err_u, err_u, w, MPFR_RNDU); /* t * 2^w */ mpfr_add (err_u, err_u, err_t, MPFR_RNDU); /* max|t| * 2^w */ if (stop >= 2) { /* take into account the neglected terms: t * 2^w */ mpfr_div_2ui (err_s, err_s, w, MPFR_RNDU); if (MPFR_IS_POS(t)) mpfr_add (err_s, err_s, t, MPFR_RNDU); else mpfr_sub (err_s, err_s, t, MPFR_RNDU); mpfr_mul_2ui (err_s, err_s, w, MPFR_RNDU); stop ++; } /* if k is odd, add to Q, otherwise to P */ else if (k & 1) { /* if k = 1 mod 4, add, otherwise subtract */ if ((k & 2) == 0) mpfr_add (Q, Q, t, MPFR_RNDN); else mpfr_sub (Q, Q, t, MPFR_RNDN); /* check if the next term is smaller than ulp(Q): if EXP(err_u) <= EXP(Q), since the current term is bounded by err_u * 2^(-w), it is bounded by ulp(Q) */ if (MPFR_EXP(err_u) <= MPFR_EXP(Q)) stop ++; else stop = 0; } else { /* if k = 0 mod 4, add, otherwise subtract */ if ((k & 2) == 0) mpfr_add (P, P, t, MPFR_RNDN); else mpfr_sub (P, P, t, MPFR_RNDN); /* check if the next term is smaller than ulp(P) */ if (MPFR_EXP(err_u) <= MPFR_EXP(P)) stop ++; else stop = 0; } mpfr_add (err_s, err_s, err_t, MPFR_RNDU); /* the sum of the rounding errors on P and Q is bounded by err_s * 2^(-w) */ /* stop when start to diverge */ if (stop < 2 && ((MPFR_IS_POS(z) && mpfr_cmp_ui (z, (k + 1) / 2) < 0) || (MPFR_IS_NEG(z) && mpfr_cmp_si (z, - ((k + 1) / 2)) > 0))) { /* if we have to stop the series because it diverges, then increasing the precision will most probably fail, since we will stop to the same point, and thus compute a very similar approximation */ diverge = 1; stop = 2; /* force stop */ } } /* the sum of the total errors on P and Q is bounded by err_s * 2^(-w) */ /* Now combine: the sum of the rounding errors on P and Q is bounded by err_s * 2^(-w), and the absolute error on s/c is bounded by 2^(1-w) */ if ((n & 1) == 0) /* n even: P * (sin + cos) + Q (cos - sin) for jn Q * (sin + cos) + P (sin - cos) for yn */ { #ifdef MPFR_JN mpfr_mul (c, c, Q, MPFR_RNDN); /* Q * (sin - cos) */ mpfr_mul (s, s, P, MPFR_RNDN); /* P * (sin + cos) */ #else mpfr_mul (c, c, P, MPFR_RNDN); /* P * (sin - cos) */ mpfr_mul (s, s, Q, MPFR_RNDN); /* Q * (sin + cos) */ #endif err = MPFR_EXP(c); if (MPFR_EXP(s) > err) err = MPFR_EXP(s); #ifdef MPFR_JN mpfr_sub (s, s, c, MPFR_RNDN); #else mpfr_add (s, s, c, MPFR_RNDN); #endif } else /* n odd: P * (sin - cos) + Q (cos + sin) for jn, Q * (sin - cos) - P (cos + sin) for yn */ { #ifdef MPFR_JN mpfr_mul (c, c, P, MPFR_RNDN); /* P * (sin - cos) */ mpfr_mul (s, s, Q, MPFR_RNDN); /* Q * (sin + cos) */ #else mpfr_mul (c, c, Q, MPFR_RNDN); /* Q * (sin - cos) */ mpfr_mul (s, s, P, MPFR_RNDN); /* P * (sin + cos) */ #endif err = MPFR_EXP(c); if (MPFR_EXP(s) > err) err = MPFR_EXP(s); #ifdef MPFR_JN mpfr_add (s, s, c, MPFR_RNDN); #else mpfr_sub (s, c, s, MPFR_RNDN); #endif } if ((n & 2) != 0) mpfr_neg (s, s, MPFR_RNDN); if (MPFR_EXP(s) > err) err = MPFR_EXP(s); /* the absolute error on s is bounded by P*err(s/c) + Q*err(s/c) + err(P)*(s/c) + err(Q)*(s/c) + 3 * 2^(err - w - 1) <= (|P|+|Q|) * 2^(1-w) + err_s * 2^(1-w) + 2^err * 2^(1-w), since |c|, |old_s| <= 2. */ err2 = (MPFR_EXP(P) >= MPFR_EXP(Q)) ? MPFR_EXP(P) + 2 : MPFR_EXP(Q) + 2; /* (|P| + |Q|) * 2^(1 - w) <= 2^(err2 - w) */ err = MPFR_EXP(err_s) >= err ? MPFR_EXP(err_s) + 2 : err + 2; /* err_s * 2^(1-w) + 2^old_err * 2^(1-w) <= 2^err * 2^(-w) */ err2 = (err >= err2) ? err + 1 : err2 + 1; /* now the absolute error on s is bounded by 2^(err2 - w) */ /* multiply by sqrt(1/(Pi*z)) */ mpfr_const_pi (c, MPFR_RNDN); /* Pi, err <= 1 */ mpfr_mul (c, c, z, MPFR_RNDN); /* err <= 2 */ mpfr_si_div (c, MPFR_IS_POS(z) ? 1 : -1, c, MPFR_RNDN); /* err <= 3 */ mpfr_sqrt (c, c, MPFR_RNDN); /* err<=5/2, thus the absolute error is bounded by 3*u*|c| for |u| <= 0.25 */ mpfr_mul (err_t, c, s, MPFR_SIGN(c)==MPFR_SIGN(s) ? MPFR_RNDU : MPFR_RNDD); mpfr_abs (err_t, err_t, MPFR_RNDU); mpfr_mul_ui (err_t, err_t, 3, MPFR_RNDU); /* 3*2^(-w)*|old_c|*|s| [see below] is bounded by err_t * 2^(-w) */ err2 += MPFR_EXP(c); /* |old_c| * 2^(err2 - w) [see below] is bounded by 2^(err2-w) */ mpfr_mul (c, c, s, MPFR_RNDN); /* the absolute error on c is bounded by 1/2 ulp(c) + 3*2^(-w)*|old_c|*|s| + |old_c| * 2^(err2 - w) */ /* compute err_t * 2^(-w) + 1/2 ulp(c) = (err_t + 2^EXP(c)) * 2^(-w) */ err = (MPFR_EXP(err_t) > MPFR_EXP(c)) ? MPFR_EXP(err_t) + 1 : MPFR_EXP(c) + 1; /* err_t * 2^(-w) + 1/2 ulp(c) <= 2^(err - w) */ /* now err_t * 2^(-w) bounds 1/2 ulp(c) + 3*2^(-w)*|old_c|*|s| */ err = (err >= err2) ? err + 1 : err2 + 1; /* the absolute error on c is bounded by 2^(err - w) */ mpfr_clear (s); mpfr_clear (P); mpfr_clear (Q); mpfr_clear (t); mpfr_clear (iz); mpfr_clear (err_t); mpfr_clear (err_s); mpfr_clear (err_u); err -= MPFR_EXP(c); if (MPFR_LIKELY (MPFR_CAN_ROUND (c, w - err, MPFR_PREC(res), r))) break; if (diverge != 0) { mpfr_set (c, z, r); /* will force inex=0 below, which means the asymptotic expansion failed */ break; } MPFR_ZIV_NEXT (loop, w); } MPFR_ZIV_FREE (loop); inex = (MPFR_IS_POS(z) || ((n & 1) == 0)) ? mpfr_set (res, c, r) : mpfr_neg (res, c, r); mpfr_clear (c); return inex; }
static int mpc_sin_cos_nonfinite (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op, mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos) /* assumes that op (that is, its real or imaginary part) is not finite */ { int overlap; mpc_t op_loc; overlap = (rop_sin == op || rop_cos == op); if (overlap) { mpc_init3 (op_loc, MPC_PREC_RE (op), MPC_PREC_IM (op)); mpc_set (op_loc, op, MPC_RNDNN); } else op_loc [0] = op [0]; if (rop_sin != NULL) { if (mpfr_nan_p (MPC_RE (op_loc)) || mpfr_nan_p (MPC_IM (op_loc))) { mpc_set (rop_sin, op_loc, rnd_sin); if (mpfr_nan_p (MPC_IM (op_loc))) { /* sin(x +i*NaN) = NaN +i*NaN, except for x=0 */ /* sin(-0 +i*NaN) = -0 +i*NaN */ /* sin(+0 +i*NaN) = +0 +i*NaN */ if (!mpfr_zero_p (MPC_RE (op_loc))) mpfr_set_nan (MPC_RE (rop_sin)); } else /* op = NaN + i*y */ if (!mpfr_inf_p (MPC_IM (op_loc)) && !mpfr_zero_p (MPC_IM (op_loc))) /* sin(NaN -i*Inf) = NaN -i*Inf */ /* sin(NaN -i*0) = NaN -i*0 */ /* sin(NaN +i*0) = NaN +i*0 */ /* sin(NaN +i*Inf) = NaN +i*Inf */ /* sin(NaN +i*y) = NaN +i*NaN, when 0<|y|<Inf */ mpfr_set_nan (MPC_IM (rop_sin)); } else if (mpfr_inf_p (MPC_RE (op_loc))) { mpfr_set_nan (MPC_RE (rop_sin)); if (!mpfr_inf_p (MPC_IM (op_loc)) && !mpfr_zero_p (MPC_IM (op_loc))) /* sin(+/-Inf +i*y) = NaN +i*NaN, when 0<|y|<Inf */ mpfr_set_nan (MPC_IM (rop_sin)); else /* sin(+/-Inf -i*Inf) = NaN -i*Inf */ /* sin(+/-Inf +i*Inf) = NaN +i*Inf */ /* sin(+/-Inf -i*0) = NaN -i*0 */ /* sin(+/-Inf +i*0) = NaN +i*0 */ mpfr_set (MPC_IM (rop_sin), MPC_IM (op_loc), MPC_RND_IM (rnd_sin)); } else if (mpfr_zero_p (MPC_RE (op_loc))) { /* sin(-0 -i*Inf) = -0 -i*Inf */ /* sin(+0 -i*Inf) = +0 -i*Inf */ /* sin(-0 +i*Inf) = -0 +i*Inf */ /* sin(+0 +i*Inf) = +0 +i*Inf */ mpc_set (rop_sin, op_loc, rnd_sin); } else { /* sin(x -i*Inf) = +Inf*(sin(x) -i*cos(x)) */ /* sin(x +i*Inf) = +Inf*(sin(x) +i*cos(x)) */ mpfr_t s, c; mpfr_init2 (s, 2); mpfr_init2 (c, 2); mpfr_sin_cos (s, c, MPC_RE (op_loc), GMP_RNDZ); mpfr_set_inf (MPC_RE (rop_sin), MPFR_SIGN (s)); mpfr_set_inf (MPC_IM (rop_sin), MPFR_SIGN (c)*MPFR_SIGN (MPC_IM (op_loc))); mpfr_clear (s); mpfr_clear (c); } } if (rop_cos != NULL) { if (mpfr_nan_p (MPC_RE (op_loc))) { /* cos(NaN + i * NaN) = NaN + i * NaN */ /* cos(NaN - i * Inf) = +Inf + i * NaN */ /* cos(NaN + i * Inf) = +Inf + i * NaN */ /* cos(NaN - i * 0) = NaN - i * 0 */ /* cos(NaN + i * 0) = NaN + i * 0 */ /* cos(NaN + i * y) = NaN + i * NaN, when y != 0 */ if (mpfr_inf_p (MPC_IM (op_loc))) mpfr_set_inf (MPC_RE (rop_cos), +1); else mpfr_set_nan (MPC_RE (rop_cos)); if (mpfr_zero_p (MPC_IM (op_loc))) mpfr_set (MPC_IM (rop_cos), MPC_IM (op_loc), MPC_RND_IM (rnd_cos)); else mpfr_set_nan (MPC_IM (rop_cos)); } else if (mpfr_nan_p (MPC_IM (op_loc))) { /* cos(-Inf + i * NaN) = NaN + i * NaN */ /* cos(+Inf + i * NaN) = NaN + i * NaN */ /* cos(-0 + i * NaN) = NaN - i * 0 */ /* cos(+0 + i * NaN) = NaN + i * 0 */ /* cos(x + i * NaN) = NaN + i * NaN, when x != 0 */ if (mpfr_zero_p (MPC_RE (op_loc))) mpfr_set (MPC_IM (rop_cos), MPC_RE (op_loc), MPC_RND_IM (rnd_cos)); else mpfr_set_nan (MPC_IM (rop_cos)); mpfr_set_nan (MPC_RE (rop_cos)); } else if (mpfr_inf_p (MPC_RE (op_loc))) { /* cos(-Inf -i*Inf) = cos(+Inf +i*Inf) = -Inf +i*NaN */ /* cos(-Inf +i*Inf) = cos(+Inf -i*Inf) = +Inf +i*NaN */ /* cos(-Inf -i*0) = cos(+Inf +i*0) = NaN -i*0 */ /* cos(-Inf +i*0) = cos(+Inf -i*0) = NaN +i*0 */ /* cos(-Inf +i*y) = cos(+Inf +i*y) = NaN +i*NaN, when y != 0 */ const int same_sign = mpfr_signbit (MPC_RE (op_loc)) == mpfr_signbit (MPC_IM (op_loc)); if (mpfr_inf_p (MPC_IM (op_loc))) mpfr_set_inf (MPC_RE (rop_cos), (same_sign ? -1 : +1)); else mpfr_set_nan (MPC_RE (rop_cos)); if (mpfr_zero_p (MPC_IM (op_loc))) mpfr_setsign (MPC_IM (rop_cos), MPC_IM (op_loc), same_sign, MPC_RND_IM(rnd_cos)); else mpfr_set_nan (MPC_IM (rop_cos)); } else if (mpfr_zero_p (MPC_RE (op_loc))) { /* cos(-0 -i*Inf) = cos(+0 +i*Inf) = +Inf -i*0 */ /* cos(-0 +i*Inf) = cos(+0 -i*Inf) = +Inf +i*0 */ const int same_sign = mpfr_signbit (MPC_RE (op_loc)) == mpfr_signbit (MPC_IM (op_loc)); mpfr_setsign (MPC_IM (rop_cos), MPC_RE (op_loc), same_sign, MPC_RND_IM (rnd_cos)); mpfr_set_inf (MPC_RE (rop_cos), +1); } else { /* cos(x -i*Inf) = +Inf*cos(x) +i*Inf*sin(x), when x != 0 */ /* cos(x +i*Inf) = +Inf*cos(x) -i*Inf*sin(x), when x != 0 */ mpfr_t s, c; mpfr_init2 (c, 2); mpfr_init2 (s, 2); mpfr_sin_cos (s, c, MPC_RE (op_loc), GMP_RNDN); mpfr_set_inf (MPC_RE (rop_cos), mpfr_sgn (c)); mpfr_set_inf (MPC_IM (rop_cos), (mpfr_sgn (MPC_IM (op_loc)) == mpfr_sgn (s) ? -1 : +1)); mpfr_clear (s); mpfr_clear (c); } } if (overlap) mpc_clear (op_loc); return MPC_INEX12 (MPC_INEX (0,0), MPC_INEX (0,0)); /* everything is exact */ }
static void consistency (void) { mpfr_t x, s1, s2, c1, c2; mpfr_exp_t emin, emax; mpfr_rnd_t rnd; unsigned int flags_sin, flags_cos, flags, flags_before, flags_ref; int inex_sin, is, inex_cos, ic, inex, inex_ref; int i; emin = mpfr_get_emin (); emax = mpfr_get_emax (); for (i = 0; i <= 10000; i++) { mpfr_init2 (x, MPFR_PREC_MIN + (randlimb () % 8)); mpfr_inits2 (MPFR_PREC_MIN + (randlimb () % 8), s1, s2, c1, c2, (mpfr_ptr) 0); if (i < 8 * MPFR_RND_MAX) { int j = i / MPFR_RND_MAX; if (j & 1) mpfr_set_emin (MPFR_EMIN_MIN); mpfr_set_si (x, (j & 2) ? 1 : -1, MPFR_RNDN); mpfr_set_exp (x, mpfr_get_emin ()); rnd = (mpfr_rnd_t) (i % MPFR_RND_MAX); flags_before = 0; if (j & 4) mpfr_set_emax (-17); } else { tests_default_random (x, 256, -5, 50, 0); rnd = RND_RAND (); flags_before = (randlimb () & 1) ? (unsigned int) (MPFR_FLAGS_ALL ^ MPFR_FLAGS_ERANGE) : (unsigned int) 0; } __gmpfr_flags = flags_before; inex_sin = mpfr_sin (s1, x, rnd); is = inex_sin < 0 ? 2 : inex_sin > 0 ? 1 : 0; flags_sin = __gmpfr_flags; __gmpfr_flags = flags_before; inex_cos = mpfr_cos (c1, x, rnd); ic = inex_cos < 0 ? 2 : inex_cos > 0 ? 1 : 0; flags_cos = __gmpfr_flags; __gmpfr_flags = flags_before; inex = mpfr_sin_cos (s2, c2, x, rnd); flags = __gmpfr_flags; inex_ref = is + 4 * ic; flags_ref = flags_sin | flags_cos; if (!(mpfr_equal_p (s1, s2) && mpfr_equal_p (c1, c2)) || inex != inex_ref || flags != flags_ref) { printf ("mpfr_sin_cos and mpfr_sin/mpfr_cos disagree on %s," " i = %d\nx = ", mpfr_print_rnd_mode (rnd), i); mpfr_dump (x); printf ("s1 = "); mpfr_dump (s1); printf ("s2 = "); mpfr_dump (s2); printf ("c1 = "); mpfr_dump (c1); printf ("c2 = "); mpfr_dump (c2); printf ("inex_sin = %d (s = %d), inex_cos = %d (c = %d), " "inex = %d (expected %d)\n", inex_sin, is, inex_cos, ic, inex, inex_ref); printf ("flags_sin = 0x%x, flags_cos = 0x%x, " "flags = 0x%x (expected 0x%x)\n", flags_sin, flags_cos, flags, flags_ref); exit (1); } mpfr_clears (x, s1, s2, c1, c2, (mpfr_ptr) 0); mpfr_set_emin (emin); mpfr_set_emax (emax); } }
int mpc_sin_cos (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op, mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos) /* Feature not documented in the texinfo file: One of rop_sin or rop_cos may be NULL, in which case it is not computed, and the corresponding ternary inexact value is set to 0 (exact). */ { if (!mpc_fin_p (op)) return mpc_sin_cos_nonfinite (rop_sin, rop_cos, op, rnd_sin, rnd_cos); else if (mpfr_zero_p (MPC_IM (op))) return mpc_sin_cos_real (rop_sin, rop_cos, op, rnd_sin, rnd_cos); else if (mpfr_zero_p (MPC_RE (op))) return mpc_sin_cos_imag (rop_sin, rop_cos, op, rnd_sin, rnd_cos); else { /* let op = a + i*b, then sin(op) = sin(a)*cosh(b) + i*cos(a)*sinh(b) and cos(op) = cos(a)*cosh(b) - i*sin(a)*sinh(b). For Re(sin(op)) (and analogously, the other parts), we use the following algorithm, with rounding to nearest for all operations and working precision w: (1) x = o(sin(a)) (2) y = o(cosh(b)) (3) r = o(x*y) then the error on r is at most 4 ulps, since we can write r = sin(a)*cosh(b)*(1+t)^3 with |t| <= 2^(-w), thus for w >= 2, r = sin(a)*cosh(b)*(1+4*t) with |t| <= 2^(-w), thus the relative error is bounded by 4*2^(-w) <= 4*ulp(r). */ mpfr_t s, c, sh, ch, sch, csh; mpfr_prec_t prec; int ok; int inex_re, inex_im, inex_sin, inex_cos; prec = 2; if (rop_sin != NULL) prec = MPC_MAX (prec, MPC_MAX_PREC (rop_sin)); if (rop_cos != NULL) prec = MPC_MAX (prec, MPC_MAX_PREC (rop_cos)); mpfr_init2 (s, 2); mpfr_init2 (c, 2); mpfr_init2 (sh, 2); mpfr_init2 (ch, 2); mpfr_init2 (sch, 2); mpfr_init2 (csh, 2); do { ok = 1; prec += mpc_ceil_log2 (prec) + 5; mpfr_set_prec (s, prec); mpfr_set_prec (c, prec); mpfr_set_prec (sh, prec); mpfr_set_prec (ch, prec); mpfr_set_prec (sch, prec); mpfr_set_prec (csh, prec); mpfr_sin_cos (s, c, MPC_RE(op), GMP_RNDN); mpfr_sinh_cosh (sh, ch, MPC_IM(op), GMP_RNDN); if (rop_sin != NULL) { /* real part of sine */ mpfr_mul (sch, s, ch, GMP_RNDN); ok = (!mpfr_number_p (sch)) || mpfr_can_round (sch, prec - 2, GMP_RNDN, GMP_RNDZ, MPC_PREC_RE (rop_sin) + (MPC_RND_RE (rnd_sin) == GMP_RNDN)); if (ok) { /* imaginary part of sine */ mpfr_mul (csh, c, sh, GMP_RNDN); ok = (!mpfr_number_p (csh)) || mpfr_can_round (csh, prec - 2, GMP_RNDN, GMP_RNDZ, MPC_PREC_IM (rop_sin) + (MPC_RND_IM (rnd_sin) == GMP_RNDN)); } } if (rop_cos != NULL && ok) { /* real part of cosine */ mpfr_mul (c, c, ch, GMP_RNDN); ok = (!mpfr_number_p (c)) || mpfr_can_round (c, prec - 2, GMP_RNDN, GMP_RNDZ, MPC_PREC_RE (rop_cos) + (MPC_RND_RE (rnd_cos) == GMP_RNDN)); if (ok) { /* imaginary part of cosine */ mpfr_mul (s, s, sh, GMP_RNDN); mpfr_neg (s, s, GMP_RNDN); ok = (!mpfr_number_p (s)) || mpfr_can_round (s, prec - 2, GMP_RNDN, GMP_RNDZ, MPC_PREC_IM (rop_cos) + (MPC_RND_IM (rnd_cos) == GMP_RNDN)); } } } while (ok == 0); if (rop_sin != NULL) { inex_re = mpfr_set (MPC_RE (rop_sin), sch, MPC_RND_RE (rnd_sin)); if (mpfr_inf_p (sch)) inex_re = mpfr_sgn (sch); inex_im = mpfr_set (MPC_IM (rop_sin), csh, MPC_RND_IM (rnd_sin)); if (mpfr_inf_p (csh)) inex_im = mpfr_sgn (csh); inex_sin = MPC_INEX (inex_re, inex_im); } else inex_sin = MPC_INEX (0,0); /* return exact if not computed */ if (rop_cos != NULL) { inex_re = mpfr_set (MPC_RE (rop_cos), c, MPC_RND_RE (rnd_cos)); if (mpfr_inf_p (c)) inex_re = mpfr_sgn (c); inex_im = mpfr_set (MPC_IM (rop_cos), s, MPC_RND_IM (rnd_cos)); if (mpfr_inf_p (s)) inex_im = mpfr_sgn (s); inex_cos = MPC_INEX (inex_re, inex_im); } else inex_cos = MPC_INEX (0,0); /* return exact if not computed */ mpfr_clear (s); mpfr_clear (c); mpfr_clear (sh); mpfr_clear (ch); mpfr_clear (sch); mpfr_clear (csh); return (MPC_INEX12 (inex_sin, inex_cos)); } }
int mpc_exp (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) { mpfr_t x, y, z; mpfr_prec_t prec; int ok = 0; int inex_re, inex_im; int saved_underflow, saved_overflow; /* special values */ if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op))) /* NaNs exp(nan +i*y) = nan -i*0 if y = -0, nan +i*0 if y = +0, nan +i*nan otherwise exp(x+i*nan) = +/-0 +/-i*0 if x=-inf, +/-inf +i*nan if x=+inf, nan +i*nan otherwise */ { if (mpfr_zero_p (mpc_imagref (op))) return mpc_set (rop, op, MPC_RNDNN); if (mpfr_inf_p (mpc_realref (op))) { if (mpfr_signbit (mpc_realref (op))) return mpc_set_ui_ui (rop, 0, 0, MPC_RNDNN); else { mpfr_set_inf (mpc_realref (rop), +1); mpfr_set_nan (mpc_imagref (rop)); return MPC_INEX(0, 0); /* Inf/NaN are exact */ } } mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); return MPC_INEX(0, 0); /* NaN is exact */ } if (mpfr_zero_p (mpc_imagref(op))) /* special case when the input is real exp(x-i*0) = exp(x) -i*0, even if x is NaN exp(x+i*0) = exp(x) +i*0, even if x is NaN */ { inex_re = mpfr_exp (mpc_realref(rop), mpc_realref(op), MPC_RND_RE(rnd)); inex_im = mpfr_set (mpc_imagref(rop), mpc_imagref(op), MPC_RND_IM(rnd)); return MPC_INEX(inex_re, inex_im); } if (mpfr_zero_p (mpc_realref (op))) /* special case when the input is imaginary */ { inex_re = mpfr_cos (mpc_realref (rop), mpc_imagref (op), MPC_RND_RE(rnd)); inex_im = mpfr_sin (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM(rnd)); return MPC_INEX(inex_re, inex_im); } if (mpfr_inf_p (mpc_realref (op))) /* real part is an infinity, exp(-inf +i*y) = 0*(cos y +i*sin y) exp(+inf +i*y) = +/-inf +i*nan if y = +/-inf +inf*(cos y +i*sin y) if 0 < |y| < inf */ { mpfr_t n; mpfr_init2 (n, 2); if (mpfr_signbit (mpc_realref (op))) mpfr_set_ui (n, 0, GMP_RNDN); else mpfr_set_inf (n, +1); if (mpfr_inf_p (mpc_imagref (op))) { inex_re = mpfr_set (mpc_realref (rop), n, GMP_RNDN); if (mpfr_signbit (mpc_realref (op))) inex_im = mpfr_set (mpc_imagref (rop), n, GMP_RNDN); else { mpfr_set_nan (mpc_imagref (rop)); inex_im = 0; /* NaN is exact */ } } else { mpfr_t c, s; mpfr_init2 (c, 2); mpfr_init2 (s, 2); mpfr_sin_cos (s, c, mpc_imagref (op), GMP_RNDN); inex_re = mpfr_copysign (mpc_realref (rop), n, c, GMP_RNDN); inex_im = mpfr_copysign (mpc_imagref (rop), n, s, GMP_RNDN); mpfr_clear (s); mpfr_clear (c); } mpfr_clear (n); return MPC_INEX(inex_re, inex_im); } if (mpfr_inf_p (mpc_imagref (op))) /* real part is finite non-zero number, imaginary part is an infinity */ { mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); return MPC_INEX(0, 0); /* NaN is exact */ } /* from now on, both parts of op are regular numbers */ prec = MPC_MAX_PREC(rop) + MPC_MAX (MPC_MAX (-mpfr_get_exp (mpc_realref (op)), 0), -mpfr_get_exp (mpc_imagref (op))); /* When op is close to 0, then exp is close to 1+Re(op), while cos is close to 1-Im(op); to decide on the ternary value of exp*cos, we need a high enough precision so that none of exp or cos is computed as 1. */ mpfr_init2 (x, 2); mpfr_init2 (y, 2); mpfr_init2 (z, 2); /* save the underflow or overflow flags from MPFR */ saved_underflow = mpfr_underflow_p (); saved_overflow = mpfr_overflow_p (); do { prec += mpc_ceil_log2 (prec) + 5; mpfr_set_prec (x, prec); mpfr_set_prec (y, prec); mpfr_set_prec (z, prec); /* FIXME: x may overflow so x.y does overflow too, while Re(exp(op)) could be represented in the precision of rop. */ mpfr_clear_overflow (); mpfr_clear_underflow (); mpfr_exp (x, mpc_realref(op), GMP_RNDN); /* error <= 0.5ulp */ mpfr_sin_cos (z, y, mpc_imagref(op), GMP_RNDN); /* errors <= 0.5ulp */ mpfr_mul (y, y, x, GMP_RNDN); /* error <= 2ulp */ ok = mpfr_overflow_p () || mpfr_zero_p (x) || mpfr_can_round (y, prec - 2, GMP_RNDN, GMP_RNDZ, MPC_PREC_RE(rop) + (MPC_RND_RE(rnd) == GMP_RNDN)); if (ok) /* compute imaginary part */ { mpfr_mul (z, z, x, GMP_RNDN); ok = mpfr_overflow_p () || mpfr_zero_p (x) || mpfr_can_round (z, prec - 2, GMP_RNDN, GMP_RNDZ, MPC_PREC_IM(rop) + (MPC_RND_IM(rnd) == GMP_RNDN)); } } while (ok == 0); inex_re = mpfr_set (mpc_realref(rop), y, MPC_RND_RE(rnd)); inex_im = mpfr_set (mpc_imagref(rop), z, MPC_RND_IM(rnd)); if (mpfr_overflow_p ()) { /* overflow in real exponential, inex is sign of infinite result */ inex_re = mpfr_sgn (y); inex_im = mpfr_sgn (z); } else if (mpfr_underflow_p ()) { /* underflow in real exponential, inex is opposite of sign of 0 result */ inex_re = (mpfr_signbit (y) ? +1 : -1); inex_im = (mpfr_signbit (z) ? +1 : -1); } mpfr_clear (x); mpfr_clear (y); mpfr_clear (z); /* restore underflow and overflow flags from MPFR */ if (saved_underflow) mpfr_set_underflow (); if (saved_overflow) mpfr_set_overflow (); return MPC_INEX(inex_re, inex_im); }
/* computes tan(x) = sign(x)*sqrt(1/cos(x)^2-1) */ int mpfr_tan (mpfr_ptr y, mpfr_srcptr x, mp_rnd_t rnd_mode) { mp_prec_t precy, m; int inexact; mpfr_t s, c; MPFR_ZIV_DECL (loop); MPFR_SAVE_EXPO_DECL (expo); MPFR_GROUP_DECL (group); MPFR_LOG_FUNC (("x[%#R]=%R rnd=%d", x, x, rnd_mode), ("y[%#R]=%R inexact=%d", y, 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 /* x is zero */ { MPFR_ASSERTD(MPFR_IS_ZERO(x)); MPFR_SET_ZERO(y); MPFR_SET_SAME_SIGN(y, x); MPFR_RET(0); } } /* tan(x) = x + x^3/3 + ... so the error is < 2^(3*EXP(x)-1) */ MPFR_FAST_COMPUTE_IF_SMALL_INPUT (y, x, -2 * MPFR_GET_EXP (x), 1, 1, rnd_mode, {}); MPFR_SAVE_EXPO_MARK (expo); /* Compute initial precision */ precy = MPFR_PREC (y); m = precy + MPFR_INT_CEIL_LOG2 (precy) + 13; MPFR_ASSERTD (m >= 2); /* needed for the error analysis in algorithms.tex */ MPFR_GROUP_INIT_2 (group, m, s, c); MPFR_ZIV_INIT (loop, m); for (;;) { /* The only way to get an overflow is to get ~ Pi/2 But the result will be ~ 2^Prec(y). */ mpfr_sin_cos (s, c, x, GMP_RNDN); /* err <= 1/2 ulp on s and c */ mpfr_div (c, s, c, GMP_RNDN); /* err <= 4 ulps */ MPFR_ASSERTD (!MPFR_IS_SINGULAR (c)); if (MPFR_LIKELY (MPFR_CAN_ROUND (c, m - 2, precy, rnd_mode))) break; MPFR_ZIV_NEXT (loop, m); MPFR_GROUP_REPREC_2 (group, m, s, c); } MPFR_ZIV_FREE (loop); inexact = mpfr_set (y, c, rnd_mode); MPFR_GROUP_CLEAR (group); MPFR_SAVE_EXPO_FREE (expo); return mpfr_check_range (y, inexact, rnd_mode); }
int mpc_tan (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) { mpc_t x, y; mpfr_prec_t prec; mpfr_exp_t err; int ok = 0; int inex; /* special values */ if (!mpc_fin_p (op)) { if (mpfr_nan_p (mpc_realref (op))) { if (mpfr_inf_p (mpc_imagref (op))) /* tan(NaN -i*Inf) = +/-0 -i */ /* tan(NaN +i*Inf) = +/-0 +i */ { /* exact unless 1 is not in exponent range */ inex = mpc_set_si_si (rop, 0, (MPFR_SIGN (mpc_imagref (op)) < 0) ? -1 : +1, rnd); } else /* tan(NaN +i*y) = NaN +i*NaN, when y is finite */ /* tan(NaN +i*NaN) = NaN +i*NaN */ { mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); inex = MPC_INEX (0, 0); /* always exact */ } } else if (mpfr_nan_p (mpc_imagref (op))) { if (mpfr_cmp_ui (mpc_realref (op), 0) == 0) /* tan(-0 +i*NaN) = -0 +i*NaN */ /* tan(+0 +i*NaN) = +0 +i*NaN */ { mpc_set (rop, op, rnd); inex = MPC_INEX (0, 0); /* always exact */ } else /* tan(x +i*NaN) = NaN +i*NaN, when x != 0 */ { mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); inex = MPC_INEX (0, 0); /* always exact */ } } else if (mpfr_inf_p (mpc_realref (op))) { if (mpfr_inf_p (mpc_imagref (op))) /* tan(-Inf -i*Inf) = -/+0 -i */ /* tan(-Inf +i*Inf) = -/+0 +i */ /* tan(+Inf -i*Inf) = +/-0 -i */ /* tan(+Inf +i*Inf) = +/-0 +i */ { const int sign_re = mpfr_signbit (mpc_realref (op)); int inex_im; mpfr_set_ui (mpc_realref (rop), 0, MPC_RND_RE (rnd)); mpfr_setsign (mpc_realref (rop), mpc_realref (rop), sign_re, MPFR_RNDN); /* exact, unless 1 is not in exponent range */ inex_im = mpfr_set_si (mpc_imagref (rop), mpfr_signbit (mpc_imagref (op)) ? -1 : +1, MPC_RND_IM (rnd)); inex = MPC_INEX (0, inex_im); } else /* tan(-Inf +i*y) = tan(+Inf +i*y) = NaN +i*NaN, when y is finite */ { mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); inex = MPC_INEX (0, 0); /* always exact */ } } else /* tan(x -i*Inf) = +0*sin(x)*cos(x) -i, when x is finite */ /* tan(x +i*Inf) = +0*sin(x)*cos(x) +i, when x is finite */ { mpfr_t c; mpfr_t s; int inex_im; mpfr_init (c); mpfr_init (s); mpfr_sin_cos (s, c, mpc_realref (op), MPFR_RNDN); mpfr_set_ui (mpc_realref (rop), 0, MPC_RND_RE (rnd)); mpfr_setsign (mpc_realref (rop), mpc_realref (rop), mpfr_signbit (c) != mpfr_signbit (s), MPFR_RNDN); /* exact, unless 1 is not in exponent range */ inex_im = mpfr_set_si (mpc_imagref (rop), (mpfr_signbit (mpc_imagref (op)) ? -1 : +1), MPC_RND_IM (rnd)); inex = MPC_INEX (0, inex_im); mpfr_clear (s); mpfr_clear (c); } return inex; } if (mpfr_zero_p (mpc_realref (op))) /* tan(-0 -i*y) = -0 +i*tanh(y), when y is finite. */ /* tan(+0 +i*y) = +0 +i*tanh(y), when y is finite. */ { int inex_im; mpfr_set (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); inex_im = mpfr_tanh (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd)); return MPC_INEX (0, inex_im); } if (mpfr_zero_p (mpc_imagref (op))) /* tan(x -i*0) = tan(x) -i*0, when x is finite. */ /* tan(x +i*0) = tan(x) +i*0, when x is finite. */ { int inex_re; inex_re = mpfr_tan (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); mpfr_set (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd)); return MPC_INEX (inex_re, 0); } /* ordinary (non-zero) numbers */ /* tan(op) = sin(op) / cos(op). We use the following algorithm with rounding away from 0 for all operations, and working precision w: (1) x = A(sin(op)) (2) y = A(cos(op)) (3) z = A(x/y) the error on Im(z) is at most 81 ulp, the error on Re(z) is at most 7 ulp if k < 2, 8 ulp if k = 2, else 5+k ulp, where k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y)) see proof in algorithms.tex. */ prec = MPC_MAX_PREC(rop); mpc_init2 (x, 2); mpc_init2 (y, 2); err = 7; do { mpfr_exp_t k, exr, eyr, eyi, ezr; ok = 0; /* FIXME: prevent addition overflow */ prec += mpc_ceil_log2 (prec) + err; mpc_set_prec (x, prec); mpc_set_prec (y, prec); /* rounding away from zero: except in the cases x=0 or y=0 (processed above), sin x and cos y are never exact, so rounding away from 0 is rounding towards 0 and adding one ulp to the absolute value */ mpc_sin_cos (x, y, op, MPC_RNDZZ, MPC_RNDZZ); MPFR_ADD_ONE_ULP (mpc_realref (x)); MPFR_ADD_ONE_ULP (mpc_imagref (x)); MPFR_ADD_ONE_ULP (mpc_realref (y)); MPFR_ADD_ONE_ULP (mpc_imagref (y)); MPC_ASSERT (mpfr_zero_p (mpc_realref (x)) == 0); if ( mpfr_inf_p (mpc_realref (x)) || mpfr_inf_p (mpc_imagref (x)) || mpfr_inf_p (mpc_realref (y)) || mpfr_inf_p (mpc_imagref (y))) { /* If the real or imaginary part of x is infinite, it means that Im(op) was large, in which case the result is sign(tan(Re(op)))*0 + sign(Im(op))*I, where sign(tan(Re(op))) = sign(Re(x))*sign(Re(y)). */ int inex_re, inex_im; mpfr_set_ui (mpc_realref (rop), 0, MPFR_RNDN); if (mpfr_sgn (mpc_realref (x)) * mpfr_sgn (mpc_realref (y)) < 0) { mpfr_neg (mpc_realref (rop), mpc_realref (rop), MPFR_RNDN); inex_re = 1; } else inex_re = -1; /* +0 is rounded down */ if (mpfr_sgn (mpc_imagref (op)) > 0) { mpfr_set_ui (mpc_imagref (rop), 1, MPFR_RNDN); inex_im = 1; } else { mpfr_set_si (mpc_imagref (rop), -1, MPFR_RNDN); inex_im = -1; } inex = MPC_INEX(inex_re, inex_im); goto end; } exr = mpfr_get_exp (mpc_realref (x)); eyr = mpfr_get_exp (mpc_realref (y)); eyi = mpfr_get_exp (mpc_imagref (y)); /* some parts of the quotient may be exact */ inex = mpc_div (x, x, y, MPC_RNDZZ); /* OP is no pure real nor pure imaginary, so in theory the real and imaginary parts of its tangent cannot be null. However due to rouding errors this might happen. Consider for example tan(1+14*I) = 1.26e-10 + 1.00*I. For small precision sin(op) and cos(op) differ only by a factor I, thus after mpc_div x = I and its real part is zero. */ if (mpfr_zero_p (mpc_realref (x)) || mpfr_zero_p (mpc_imagref (x))) { err = prec; /* double precision */ continue; } if (MPC_INEX_RE (inex)) MPFR_ADD_ONE_ULP (mpc_realref (x)); if (MPC_INEX_IM (inex)) MPFR_ADD_ONE_ULP (mpc_imagref (x)); MPC_ASSERT (mpfr_zero_p (mpc_realref (x)) == 0); ezr = mpfr_get_exp (mpc_realref (x)); /* FIXME: compute k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y)) avoiding overflow */ k = exr - ezr + MPC_MAX(-eyr, eyr - 2 * eyi); err = k < 2 ? 7 : (k == 2 ? 8 : (5 + k)); /* Can the real part be rounded? */ ok = (!mpfr_number_p (mpc_realref (x))) || mpfr_can_round (mpc_realref(x), prec - err, MPFR_RNDN, MPFR_RNDZ, MPC_PREC_RE(rop) + (MPC_RND_RE(rnd) == MPFR_RNDN)); if (ok) { /* Can the imaginary part be rounded? */ ok = (!mpfr_number_p (mpc_imagref (x))) || mpfr_can_round (mpc_imagref(x), prec - 6, MPFR_RNDN, MPFR_RNDZ, MPC_PREC_IM(rop) + (MPC_RND_IM(rnd) == MPFR_RNDN)); } } while (ok == 0); inex = mpc_set (rop, x, rnd); end: mpc_clear (x); mpc_clear (y); return inex; }
/* put in rop the value of exp(2*i*pi*k/n) rounded according to rnd */ int mpc_rootofunity (mpc_ptr rop, unsigned long n, unsigned long k, mpc_rnd_t rnd) { unsigned long g; mpq_t kn; mpfr_t t, s, c; mpfr_prec_t prec; int inex_re, inex_im; mpfr_rnd_t rnd_re, rnd_im; if (n == 0) { /* Compute exp (0 + i*inf). */ mpfr_set_nan (mpc_realref (rop)); mpfr_set_nan (mpc_imagref (rop)); return MPC_INEX (0, 0); } /* Eliminate common denominator. */ k %= n; g = gcd (k, n); k /= g; n /= g; /* Now 0 <= k < n and gcd(k,n)=1. */ /* We assume that only n=1, 2, 3, 4, 6 and 12 may yield exact results and treat them separately; n=8 is also treated here for efficiency reasons. */ if (n == 1) { /* necessarily k=0 thus we want exp(0)=1 */ MPC_ASSERT (k == 0); return mpc_set_ui_ui (rop, 1, 0, rnd); } else if (n == 2) { /* since gcd(k,n)=1, necessarily k=1, thus we want exp(i*pi)=-1 */ MPC_ASSERT (k == 1); return mpc_set_si_si (rop, -1, 0, rnd); } else if (n == 4) { /* since gcd(k,n)=1, necessarily k=1 or k=3, thus we want exp(2*i*pi/4)=i or exp(2*i*pi*3/4)=-i */ MPC_ASSERT (k == 1 || k == 3); if (k == 1) return mpc_set_ui_ui (rop, 0, 1, rnd); else return mpc_set_si_si (rop, 0, -1, rnd); } else if (n == 3 || n == 6) { MPC_ASSERT ((n == 3 && (k == 1 || k == 2)) || (n == 6 && (k == 1 || k == 5))); /* for n=3, necessarily k=1 or k=2: -1/2+/-1/2*sqrt(3)*i; for n=6, necessarily k=1 or k=5: 1/2+/-1/2*sqrt(3)*i */ inex_re = mpfr_set_si (mpc_realref (rop), (n == 3 ? -1 : 1), MPC_RND_RE (rnd)); /* inverse the rounding mode for -sqrt(3)/2 for zeta_3^2 and zeta_6^5 */ rnd_im = MPC_RND_IM (rnd); if (k != 1) rnd_im = INV_RND (rnd_im); inex_im = mpfr_sqrt_ui (mpc_imagref (rop), 3, rnd_im); mpc_div_2ui (rop, rop, 1, MPC_RNDNN); if (k != 1) { mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), MPFR_RNDN); inex_im = -inex_im; } return MPC_INEX (inex_re, inex_im); } else if (n == 12) { /* necessarily k=1, 5, 7, 11: k=1: 1/2*sqrt(3) + 1/2*I k=5: -1/2*sqrt(3) + 1/2*I k=7: -1/2*sqrt(3) - 1/2*I k=11: 1/2*sqrt(3) - 1/2*I */ MPC_ASSERT (k == 1 || k == 5 || k == 7 || k == 11); /* inverse the rounding mode for -sqrt(3)/2 for zeta_12^5 and zeta_12^7 */ rnd_re = MPC_RND_RE (rnd); if (k == 5 || k == 7) rnd_re = INV_RND (rnd_re); inex_re = mpfr_sqrt_ui (mpc_realref (rop), 3, rnd_re); inex_im = mpfr_set_si (mpc_imagref (rop), k < 6 ? 1 : -1, MPC_RND_IM (rnd)); mpc_div_2ui (rop, rop, 1, MPC_RNDNN); if (k == 5 || k == 7) { mpfr_neg (mpc_realref (rop), mpc_realref (rop), MPFR_RNDN); inex_re = -inex_re; } return MPC_INEX (inex_re, inex_im); } else if (n == 8) { /* k=1, 3, 5 or 7: k=1: (1/2*I + 1/2)*sqrt(2) k=3: (1/2*I - 1/2)*sqrt(2) k=5: -(1/2*I + 1/2)*sqrt(2) k=7: -(1/2*I - 1/2)*sqrt(2) */ MPC_ASSERT (k == 1 || k == 3 || k == 5 || k == 7); rnd_re = MPC_RND_RE (rnd); if (k == 3 || k == 5) rnd_re = INV_RND (rnd_re); rnd_im = MPC_RND_IM (rnd); if (k > 4) rnd_im = INV_RND (rnd_im); inex_re = mpfr_sqrt_ui (mpc_realref (rop), 2, rnd_re); inex_im = mpfr_sqrt_ui (mpc_imagref (rop), 2, rnd_im); mpc_div_2ui (rop, rop, 1, MPC_RNDNN); if (k == 3 || k == 5) { mpfr_neg (mpc_realref (rop), mpc_realref (rop), MPFR_RNDN); inex_re = -inex_re; } if (k > 4) { mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), MPFR_RNDN); inex_im = -inex_im; } return MPC_INEX (inex_re, inex_im); } prec = MPC_MAX_PREC(rop); /* For the error analysis justifying the following algorithm, see algorithms.tex. */ mpfr_init2 (t, 67); mpfr_init2 (s, 67); mpfr_init2 (c, 67); mpq_init (kn); mpq_set_ui (kn, k, n); mpq_mul_2exp (kn, kn, 1); /* kn=2*k/n < 2 */ do { prec += mpc_ceil_log2 (prec) + 5; /* prec >= 6 */ mpfr_set_prec (t, prec); mpfr_set_prec (s, prec); mpfr_set_prec (c, prec); mpfr_const_pi (t, MPFR_RNDN); mpfr_mul_q (t, t, kn, MPFR_RNDN); mpfr_sin_cos (s, c, t, MPFR_RNDN); } while ( !mpfr_can_round (c, prec - (4 - mpfr_get_exp (c)), MPFR_RNDN, MPFR_RNDZ, MPC_PREC_RE(rop) + (MPC_RND_RE(rnd) == MPFR_RNDN)) || !mpfr_can_round (s, prec - (4 - mpfr_get_exp (s)), MPFR_RNDN, MPFR_RNDZ, MPC_PREC_IM(rop) + (MPC_RND_IM(rnd) == MPFR_RNDN))); inex_re = mpfr_set (mpc_realref(rop), c, MPC_RND_RE(rnd)); inex_im = mpfr_set (mpc_imagref(rop), s, MPC_RND_IM(rnd)); mpfr_clear (t); mpfr_clear (s); mpfr_clear (c); mpq_clear (kn); return MPC_INEX(inex_re, inex_im); }
static void _assympt_mpfr (gulong l, mpq_t q, mpfr_ptr res, mp_rnd_t rnd) { NcmBinSplit **bs_ptr = _ncm_mpsf_sbessel_get_bs (); NcmBinSplit *bs = *bs_ptr; _binsplit_spherical_bessel *data = (_binsplit_spherical_bessel *) bs->userdata; gulong prec = mpfr_get_prec (res); #define sin_x data->sin #define cos_x data->cos mpfr_set_prec (sin_x, prec); mpfr_set_prec (cos_x, prec); mpfr_set_q (res, q, rnd); mpfr_sin_cos (sin_x, cos_x, res, rnd); switch (l % 4) { case 0: break; case 1: mpfr_swap (sin_x, cos_x); mpfr_neg (sin_x, sin_x, rnd); break; case 2: mpfr_neg (sin_x, sin_x, rnd); mpfr_neg (cos_x, cos_x, rnd); break; case 3: mpfr_swap (sin_x, cos_x); mpfr_neg (cos_x, cos_x, rnd); break; } if (l > 0) { mpfr_mul_ui (cos_x, cos_x, l * (l + 1), rnd); mpfr_div (cos_x, cos_x, res, rnd); mpfr_div (cos_x, cos_x, res, rnd); mpfr_div_2ui (cos_x, cos_x, 1, rnd); } mpfr_div (sin_x, sin_x, res, rnd); data->l = l; mpq_inv (data->mq2_2, q); mpq_mul (data->mq2_2, data->mq2_2, data->mq2_2); mpq_neg (data->mq2_2, data->mq2_2); mpq_div_2exp (data->mq2_2, data->mq2_2, 2); data->sincos = 0; binsplit_spherical_bessel_assympt (bs, 0, (l + 1) / 2 + (l + 1) % 2); mpfr_mul_z (sin_x, sin_x, bs->T, rnd); mpfr_div_z (sin_x, sin_x, bs->Q, rnd); data->sincos = 1; if (l > 0) { binsplit_spherical_bessel_assympt (bs, 0, l / 2 + l % 2); mpfr_mul_z (cos_x, cos_x, bs->T, rnd); mpfr_div_z (cos_x, cos_x, bs->Q, rnd); mpfr_add (res, sin_x, cos_x, rnd); } else mpfr_set (res, sin_x, rnd); ncm_memory_pool_return (bs_ptr); return; }
static void overflowed_sin_cos0 (void) { mpfr_t x, y, z; int emax, inex, rnd, err = 0; mpfr_exp_t old_emax; old_emax = mpfr_get_emax (); mpfr_init2 (x, 8); mpfr_init2 (y, 8); mpfr_init2 (z, 8); for (emax = -1; emax <= 0; emax++) { mpfr_set_ui_2exp (z, 1, emax, MPFR_RNDN); mpfr_nextbelow (z); set_emax (emax); /* 1 is not representable. */ /* and if emax < 0, 1 - eps is not representable either. */ RND_LOOP (rnd) { mpfr_set_si (x, 0, MPFR_RNDN); mpfr_neg (x, x, MPFR_RNDN); mpfr_clear_flags (); inex = mpfr_sin_cos (x, y, x, (mpfr_rnd_t) rnd); if (! mpfr_overflow_p ()) { printf ("Error in overflowed_sin_cos0 (rnd = %s):\n" " The overflow flag is not set.\n", mpfr_print_rnd_mode ((mpfr_rnd_t) rnd)); err = 1; } if (! (mpfr_zero_p (x) && MPFR_IS_NEG (x))) { printf ("Error in overflowed_sin_cos0 (rnd = %s):\n" " Got sin = ", mpfr_print_rnd_mode ((mpfr_rnd_t) rnd)); mpfr_print_binary (x); printf (" instead of -0.\n"); err = 1; } if (rnd == MPFR_RNDZ || rnd == MPFR_RNDD) { if (inex == 0) { printf ("Error in overflowed_sin_cos0 (rnd = %s):\n" " The inexact value must be non-zero.\n", mpfr_print_rnd_mode ((mpfr_rnd_t) rnd)); err = 1; } if (! mpfr_equal_p (y, z)) { printf ("Error in overflowed_sin_cos0 (rnd = %s):\n" " Got cos = ", mpfr_print_rnd_mode ((mpfr_rnd_t) rnd)); mpfr_print_binary (y); printf (" instead of 0.11111111E%d.\n", emax); err = 1; } } else { if (inex == 0) { printf ("Error in overflowed_sin_cos0 (rnd = %s):\n" " The inexact value must be non-zero.\n", mpfr_print_rnd_mode ((mpfr_rnd_t) rnd)); err = 1; } if (! (mpfr_inf_p (y) && MPFR_IS_POS (y))) { printf ("Error in overflowed_sin_cos0 (rnd = %s):\n" " Got cos = ", mpfr_print_rnd_mode ((mpfr_rnd_t) rnd)); mpfr_print_binary (y); printf (" instead of +Inf.\n"); err = 1; } } } set_emax (old_emax); } if (err) exit (1); mpfr_clear (x); mpfr_clear (y); mpfr_clear (z); }
int main (int argc, char *argv[]) { mpfr_t x, c, s, c2, s2; tests_start_mpfr (); check_regression (); check_nans (); /* worst case from PhD thesis of Vincent Lefe`vre: x=8980155785351021/2^54 */ check53 ("4.984987858808754279e-1", "4.781075595393330379e-1", MPFR_RNDN); check53 ("4.984987858808754279e-1", "4.781075595393329824e-1", MPFR_RNDD); check53 ("4.984987858808754279e-1", "4.781075595393329824e-1", MPFR_RNDZ); check53 ("4.984987858808754279e-1", "4.781075595393330379e-1", MPFR_RNDU); check53 ("1.00031274099908640274", "8.416399183372403892e-1", MPFR_RNDN); check53 ("1.00229256850978698523", "8.427074524447979442e-1", MPFR_RNDZ); check53 ("1.00288304857059840103", "8.430252033025980029e-1", MPFR_RNDZ); check53 ("1.00591265847407274059", "8.446508805292128885e-1", MPFR_RNDN); /* Other worst cases showing a bug introduced on 2005-01-29 in rev 3248 */ check53b ("1.0111001111010111010111111000010011010001110001111011e-21", "1.0111001111010111010111111000010011010001101001110001e-21", MPFR_RNDU); check53b ("1.1011101111111010000001010111000010000111100100101101", "1.1111100100101100001111100000110011110011010001010101e-1", MPFR_RNDU); mpfr_init2 (x, 2); mpfr_set_str (x, "0.5", 10, MPFR_RNDN); test_sin (x, x, MPFR_RNDD); if (mpfr_cmp_ui_2exp (x, 3, -3)) /* x != 0.375 = 3/8 */ { printf ("mpfr_sin(0.5, MPFR_RNDD) failed with precision=2\n"); exit (1); } /* bug found by Kevin Ryde */ mpfr_const_pi (x, MPFR_RNDN); mpfr_mul_ui (x, x, 3L, MPFR_RNDN); mpfr_div_ui (x, x, 2L, MPFR_RNDN); test_sin (x, x, MPFR_RNDN); if (mpfr_cmp_ui (x, 0) >= 0) { printf ("Error: wrong sign for sin(3*Pi/2)\n"); exit (1); } /* Can fail on an assert */ mpfr_set_prec (x, 53); mpfr_set_str (x, "77291789194529019661184401408", 10, MPFR_RNDN); mpfr_init2 (c, 4); mpfr_init2 (s, 42); mpfr_init2 (c2, 4); mpfr_init2 (s2, 42); test_sin (s, x, MPFR_RNDN); mpfr_cos (c, x, MPFR_RNDN); mpfr_sin_cos (s2, c2, x, MPFR_RNDN); if (mpfr_cmp (c2, c)) { printf("cos differs for x=77291789194529019661184401408"); exit (1); } if (mpfr_cmp (s2, s)) { printf("sin differs for x=77291789194529019661184401408"); exit (1); } mpfr_set_str_binary (x, "1.1001001000011111101101010100010001000010110100010011"); test_sin (x, x, MPFR_RNDZ); if (mpfr_cmp_str (x, "1.1111111111111111111111111111111111111111111111111111e-1", 2, MPFR_RNDN)) { printf ("Error for x= 1.1001001000011111101101010100010001000010110100010011\nGot "); mpfr_dump (x); exit (1); } mpfr_set_prec (s, 9); mpfr_set_prec (x, 190); mpfr_const_pi (x, MPFR_RNDN); mpfr_sin (s, x, MPFR_RNDZ); if (mpfr_cmp_str (s, "0.100000101e-196", 2, MPFR_RNDN)) { printf ("Error for x ~= pi\n"); mpfr_dump (s); exit (1); } mpfr_clear (s2); mpfr_clear (c2); mpfr_clear (s); mpfr_clear (c); mpfr_clear (x); test_generic (2, 100, 15); test_generic (MPFR_SINCOS_THRESHOLD-1, MPFR_SINCOS_THRESHOLD+1, 2); test_sign (); check_tiny (); data_check ("data/sin", mpfr_sin, "mpfr_sin"); bad_cases (mpfr_sin, mpfr_asin, "mpfr_sin", 256, -40, 0, 4, 128, 800, 50); tests_end_mpfr (); return 0; }
int mpc_cos (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) { mpfr_t x, y, z; mp_prec_t prec; int ok = 0; int inex_re, inex_im; /* special values */ if (!mpfr_number_p (MPC_RE (op)) || !mpfr_number_p (MPC_IM (op))) { if (mpfr_nan_p (MPC_RE (op))) { /* cos(NaN + i * NaN) = NaN + i * NaN */ /* cos(NaN - i * Inf) = +Inf + i * NaN */ /* cos(NaN + i * Inf) = +Inf + i * NaN */ /* cos(NaN - i * 0) = NaN - i * 0 */ /* cos(NaN + i * 0) = NaN + i * 0 */ /* cos(NaN + i * y) = NaN + i * NaN, when y != 0 */ if (mpfr_inf_p (MPC_IM (op))) mpfr_set_inf (MPC_RE (rop), +1); else mpfr_set_nan (MPC_RE (rop)); if (mpfr_zero_p (MPC_IM (op))) mpfr_set (MPC_IM (rop), MPC_IM (op), MPC_RND_IM (rnd)); else mpfr_set_nan (MPC_IM (rop)); } else if (mpfr_nan_p (MPC_IM (op))) { /* cos(-Inf + i * NaN) = NaN + i * NaN */ /* cos(+Inf + i * NaN) = NaN + i * NaN */ /* cos(-0 + i * NaN) = NaN - i * 0 */ /* cos(+0 + i * NaN) = NaN + i * 0 */ /* cos(x + i * NaN) = NaN + i * NaN, when x != 0 */ if (mpfr_zero_p (MPC_RE (op))) mpfr_set (MPC_IM (rop), MPC_RE (op), MPC_RND_IM (rnd)); else mpfr_set_nan (MPC_IM (rop)); mpfr_set_nan (MPC_RE (rop)); } else if (mpfr_inf_p (MPC_RE (op))) { /* cos(-Inf -i*Inf) = cos(+Inf +i*Inf) = -Inf +i*NaN */ /* cos(-Inf +i*Inf) = cos(+Inf -i*Inf) = +Inf +i*NaN */ /* cos(-Inf -i*0) = cos(+Inf +i*0) = NaN -i*0 */ /* cos(-Inf +i*0) = cos(+Inf -i*0) = NaN +i*0 */ /* cos(-Inf +i*y) = cos(+Inf +i*y) = NaN +i*NaN, when y != 0 */ /* SAME_SIGN is useful only if op == (+/-)Inf + i * (+/-)0, but, as MPC_RE(OP) might be erased (when ROP == OP), we compute it now */ const int same_sign = mpfr_signbit (MPC_RE (op)) == mpfr_signbit (MPC_IM (op)); if (mpfr_inf_p (MPC_IM (op))) mpfr_set_inf (MPC_RE (rop), (same_sign ? -1 : +1)); else mpfr_set_nan (MPC_RE (rop)); if (mpfr_zero_p (MPC_IM (op))) mpfr_setsign (MPC_IM (rop), MPC_IM (op), same_sign, MPC_RND_IM(rnd)); else mpfr_set_nan (MPC_IM (rop)); } else if (mpfr_zero_p (MPC_RE (op))) { /* cos(-0 -i*Inf) = cos(+0 +i*Inf) = +Inf -i*0 */ /* cos(-0 +i*Inf) = cos(+0 -i*Inf) = +Inf +i*0 */ const int same_sign = mpfr_signbit (MPC_RE (op)) == mpfr_signbit (MPC_IM (op)); mpfr_setsign (MPC_IM (rop), MPC_RE (op), same_sign, MPC_RND_IM (rnd)); mpfr_set_inf (MPC_RE (rop), +1); } else { /* cos(x -i*Inf) = +Inf*cos(x) +i*Inf*sin(x), when x != 0 */ /* cos(x +i*Inf) = +Inf*cos(x) -i*Inf*sin(x), when x != 0 */ mpfr_t c; mpfr_t s; mpfr_init (c); mpfr_init (s); mpfr_sin_cos (s, c, MPC_RE (op), GMP_RNDN); mpfr_set_inf (MPC_RE (rop), mpfr_sgn (c)); mpfr_set_inf (MPC_IM (rop), (mpfr_sgn (MPC_IM (op)) == mpfr_sgn (s) ? -1 : +1)); mpfr_clear (s); mpfr_clear (c); } return MPC_INEX (0, 0); /* always exact */ } if (mpfr_zero_p (MPC_RE (op))) { /* cos(-0 - i * y) = cos(+0 + i * y) = cosh(y) - i * 0 cos(-0 + i * y) = cos(+0 - i * y) = cosh(y) + i * 0, when y >= 0 */ /* When ROP == OP, the sign of 0 will be erased, so use it now */ const int imag_sign = mpfr_signbit (MPC_RE (op)) == mpfr_signbit (MPC_IM (op)); if (mpfr_zero_p (MPC_IM (op))) inex_re = mpfr_set_ui (MPC_RE (rop), 1, MPC_RND_RE (rnd)); else inex_re = mpfr_cosh (MPC_RE (rop), MPC_IM (op), MPC_RND_RE (rnd)); mpfr_set_ui (MPC_IM (rop), 0, MPC_RND_IM (rnd)); mpfr_setsign (MPC_IM (rop), MPC_IM (rop), imag_sign, MPC_RND_IM (rnd)); return MPC_INEX (inex_re, 0); } if (mpfr_zero_p (MPC_IM (op))) { /* cos(x +i*0) = cos(x) -i*0*sign(sin(x)) */ /* cos(x -i*0) = cos(x) +i*0*sign(sin(x)) */ mpfr_t sign; mpfr_init2 (sign, 2); mpfr_sin (sign, MPC_RE (op), GMP_RNDN); if (!mpfr_signbit (MPC_IM (op))) MPFR_CHANGE_SIGN (sign); inex_re = mpfr_cos (MPC_RE (rop), MPC_RE (op), MPC_RND_RE (rnd)); mpfr_set_ui (MPC_IM (rop), 0ul, GMP_RNDN); if (mpfr_signbit (sign)) MPFR_CHANGE_SIGN (MPC_IM (rop)); mpfr_clear (sign); return MPC_INEX (inex_re, 0); } /* ordinary (non-zero) numbers */ /* let op = a + i*b, then cos(op) = cos(a)*cosh(b) - i*sin(a)*sinh(b). We use the following algorithm (same for the imaginary part), with rounding to nearest for all operations, and working precision w: (1) x = o(cos(a)) (2) y = o(cosh(b)) (3) r = o(x*y) then the error on r is at most 4 ulps, since we can write r = cos(a)*cosh(b)*(1+t)^3 with |t| <= 2^(-w), thus for w >= 2, r = cos(a)*cosh(b)*(1+4*t) with |t| <= 2^(-w), thus the relative error is bounded by 4*2^(-w) <= 4*ulp(r). */ prec = MPC_MAX_PREC(rop); mpfr_init2 (x, 2); mpfr_init2 (y, 2); mpfr_init2 (z, 2); do { prec += mpc_ceil_log2 (prec) + 5; mpfr_set_prec (x, prec); mpfr_set_prec (y, prec); mpfr_set_prec (z, prec); mpfr_sin_cos (y, x, MPC_RE(op), GMP_RNDN); mpfr_cosh (z, MPC_IM(op), GMP_RNDN); mpfr_mul (x, x, z, GMP_RNDN); ok = mpfr_can_round (x, prec - 2, GMP_RNDN, GMP_RNDZ, MPFR_PREC(MPC_RE(rop)) + (MPC_RND_RE(rnd) == GMP_RNDN)); if (ok) /* compute imaginary part */ { mpfr_sinh (z, MPC_IM(op), GMP_RNDN); mpfr_mul (y, y, z, GMP_RNDN); mpfr_neg (y, y, GMP_RNDN); ok = mpfr_can_round (y, prec - 2, GMP_RNDN, GMP_RNDZ, MPFR_PREC(MPC_IM(rop)) + (MPC_RND_IM(rnd) == GMP_RNDN)); } } while (ok == 0); inex_re = mpfr_set (MPC_RE(rop), x, MPC_RND_RE(rnd)); inex_im = mpfr_set (MPC_IM(rop), y, MPC_RND_IM(rnd)); mpfr_clear (x); mpfr_clear (y); mpfr_clear (z); return MPC_INEX (inex_re, inex_im); }