代码拉取完成,页面将自动刷新
/*
Copyright (C) 2012-2014 Fredrik Johansson
This file is part of Arb.
Arb is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See <http://www.gnu.org/licenses/>.
*/
#include "flint/mpn_extras.h"
#include "arb.h"
#define TMP_ALLOC_LIMBS(__n) TMP_ALLOC((__n) * sizeof(mp_limb_t))
#define MAGLIM(prec) FLINT_MAX(65536, (4*prec))
static void
mag_nonzero_fast_mul(mag_t z, const mag_t x, const mag_t y)
{
MAG_MAN(z) = MAG_FIXMUL(MAG_MAN(x), MAG_MAN(y)) + LIMB_ONE;
MAG_EXP(z) = MAG_EXP(x) + MAG_EXP(y);
MAG_FAST_ADJUST_ONE_TOO_SMALL(z);
}
static void
mag_nonzero_fast_add(mag_t z, const mag_t x, const mag_t y)
{
slong shift = MAG_EXP(x) - MAG_EXP(y);
if (shift == 0)
{
MAG_EXP(z) = MAG_EXP(x);
MAG_MAN(z) = MAG_MAN(x) + MAG_MAN(y);
MAG_FAST_ADJUST_ONE_TOO_LARGE(z); /* may need two adjustments */
}
else if (shift > 0)
{
MAG_EXP(z) = MAG_EXP(x);
if (shift >= MAG_BITS)
MAG_MAN(z) = MAG_MAN(x) + LIMB_ONE;
else
MAG_MAN(z) = MAG_MAN(x) + (MAG_MAN(y) >> shift) + LIMB_ONE;
}
else
{
shift = -shift;
MAG_EXP(z) = MAG_EXP(y);
if (shift >= MAG_BITS)
MAG_MAN(z) = MAG_MAN(y) + LIMB_ONE;
else
MAG_MAN(z) = MAG_MAN(y) + (MAG_MAN(x) >> shift) + LIMB_ONE;
}
MAG_FAST_ADJUST_ONE_TOO_LARGE(z);
}
static int
mag_nonzero_fast_cmp(const mag_t x, const mag_t y)
{
if (MAG_EXP(x) == MAG_EXP(y))
return (MAG_MAN(x) < MAG_MAN(y)) ? -1 : 1;
else
return (MAG_EXP(x) < MAG_EXP(y)) ? -1 : 1;
}
static void
mag_fast_set(mag_t x, const mag_t y)
{
MAG_EXP(x) = MAG_EXP(y);
MAG_MAN(x) = MAG_MAN(y);
}
void
_arb_sin_cos(arb_t zsin, arb_t zcos, const arf_t x, const mag_t xrad, slong prec)
{
int want_sin, want_cos;
slong radexp, exp, wp, wn, N, r, wprounded, maglim, orig_prec;
mp_ptr tmp, w, sina, cosa, sinb, cosb, ta, tb;
mp_ptr sinptr, cosptr;
mp_limb_t p1, q1bits, p2, q2bits, error, error2, p1_tab1, radman;
int negative, inexact, octant;
int sinnegative, cosnegative, swapsincos;
TMP_INIT;
/* PART 1: special cases and setup. */
orig_prec = prec;
/* Below, both x and xrad will be finite, and x will be nonzero. */
if (mag_is_inf(xrad) || arf_is_special(x))
{
_arb_sin_cos_generic(zsin, zcos, x, xrad, prec);
return;
}
exp = ARF_EXP(x);
maglim = MAGLIM(prec);
negative = ARF_SGNBIT(x);
/* Unlikely: tiny or huge midpoint. As written, this test also
catches any bignum exponents. */
if (exp < -(prec/2) - 2 || exp > maglim)
{
_arb_sin_cos_generic(zsin, zcos, x, xrad, prec);
return;
}
want_sin = (zsin != NULL);
want_cos = (zcos != NULL);
/* Copy the radius data. */
radexp = MAG_EXP(xrad);
radman = MAG_MAN(xrad);
if (radman != 0)
{
/* Clamp the radius exponent to a safe range. */
if (radexp < MAG_MIN_LAGOM_EXP || radexp > MAG_MAX_LAGOM_EXP)
{
/* Very wide... */
if (fmpz_sgn(MAG_EXPREF(xrad)) > 0)
{
_arb_sin_cos_wide(zsin, zcos, x, xrad, prec);
return;
}
radman = MAG_ONE_HALF;
radexp = MAG_MIN_LAGOM_EXP + 1;
}
/* Use wide algorithm. */
if (radexp >= -24)
{
_arb_sin_cos_wide(zsin, zcos, x, xrad, prec);
return;
}
/* Regular case: decrease precision to match generic max. accuracy. */
/* Note: near x=0, the error can be quadratic for cos. */
if (want_cos && exp < -2)
prec = FLINT_MIN(prec, 20 - FLINT_MAX(exp, radexp) - radexp);
else
prec = FLINT_MIN(prec, 20 - radexp);
}
if (prec >= ARB_SIN_COS_ATAN_REDUCTION_PREC && prec <= ARB_SIN_COS_ATAN_REDUCTION_DEFAULT_MAX_PREC)
{
mag_t err;
mag_init(err);
mag_set(err, xrad);
arb_sin_cos_arf_atan_reduction(zsin, zcos, x, prec);
if (zsin != NULL) arb_add_error_mag(zsin, err);
if (zcos != NULL) arb_add_error_mag(zcos, err);
mag_clear(err);
return;
}
/* Absolute working precision (NOT rounded to a limb multiple) */
wp = prec + 8;
if (want_sin && exp <= 0)
wp += (-exp);
/* Number of limbs */
wn = (wp + FLINT_BITS - 1) / FLINT_BITS;
/* Precision rounded to a number of bits */
wprounded = FLINT_BITS * wn;
/* Don't be close to the boundary (to allow adding adding the
Taylor series truncation error without overflow) */
wp = FLINT_MAX(wp, wprounded - (FLINT_BITS - 4));
/* Too high precision to use table -- use generic algorithm */
if (wp > ARB_SIN_COS_TAB2_PREC)
{
_arb_sin_cos_generic(zsin, zcos, x, xrad, orig_prec);
return;
}
/* PART 2: the actual computation. */
TMP_START;
tmp = TMP_ALLOC_LIMBS(9 * wn);
w = tmp; /* requires wn limbs */
sina = w + wn; /* requires wn limbs */
cosa = sina + wn; /* requires wn limbs */
sinb = cosa + wn; /* requires wn limbs */
cosb = sinb + wn; /* requires wn limbs */
ta = cosb + wn; /* requires 2*wn limbs */
tb = ta + 2*wn; /* requires 2*wn limbs */
/* reduce modulo pi/4 */
if (_arb_get_mpn_fixed_mod_pi4(w, NULL, &octant, &error, x, wn) == 0)
{
/* may run out of precision for pi/4 */
_arb_sin_cos_generic(zsin, zcos, x, xrad, orig_prec);
TMP_END;
return;
}
sinnegative = (octant >= 4) ^ negative;
cosnegative = (octant >= 2 && octant <= 5);
swapsincos = (octant == 1 || octant == 2 || octant == 5 || octant == 6);
/* Table-based argument reduction (1 or 2 steps) */
if (wp <= ARB_SIN_COS_TAB1_PREC)
{
q1bits = ARB_SIN_COS_TAB1_BITS;
q2bits = 0;
p1 = p1_tab1 = w[wn-1] >> (FLINT_BITS - q1bits);
w[wn-1] -= (p1 << (FLINT_BITS - q1bits));
p2 = 0;
/* p1_tab1 will be used for the error bounds at the end. */
p1_tab1 = p1;
}
else
{
q1bits = ARB_SIN_COS_TAB21_BITS;
q2bits = ARB_SIN_COS_TAB21_BITS + ARB_SIN_COS_TAB22_BITS;
/* p1_tab1 will be used for the error bounds at the end. */
p1_tab1 = w[wn-1] >> (FLINT_BITS - ARB_SIN_COS_TAB1_BITS);
p1 = w[wn-1] >> (FLINT_BITS - q1bits);
w[wn-1] -= (p1 << (FLINT_BITS - q1bits));
p2 = w[wn-1] >> (FLINT_BITS - q2bits);
w[wn-1] -= (p2 << (FLINT_BITS - q2bits));
}
/* |w| <= 2^-r */
r = _arb_mpn_leading_zeros(w, wn);
/* Choose number of terms N such that Taylor series truncation
error is <= 2^-wp */
N = _arb_exp_taylor_bound(-r, wp);
/* the summation for sin/cos is actually done to (2N-1)! */
N = (N + 1) / 2;
if (N < 14)
{
/* Evaluate Taylor series */
_arb_sin_cos_taylor_rs(sina, cosa, &error2, w, wn, N, 0, 1);
/* Taylor series evaluation error */
error += error2;
/* Taylor series truncation error */
error += UWORD(1) << (wprounded-wp);
}
else /* Compute cos(a) from sin(a) using a square root. */
{
/* Evaluate Taylor series */
_arb_sin_cos_taylor_rs(sina, cosa, &error2, w, wn, N, 1, 1);
error += error2;
error += UWORD(1) << (wprounded-wp);
if (flint_mpn_zero_p(sina, wn))
{
flint_mpn_store(cosa, wn, LIMB_ONES);
error = FLINT_MAX(error, 1);
}
else
{
mpn_sqr(ta, sina, wn);
/* 1 - s^2 (negation guaranteed to have borrow) */
mpn_neg(ta, ta, 2 * wn);
/* top limb of ta must be nonzero, so no need to normalize
before calling mpn_sqrtrem */
mpn_sqrtrem(cosa, ta, ta, 2 * wn);
/* When converting sin to cos, the error for cos must be
smaller than the error for sin; but we also get 1 ulp
extra error from the square root. */
error += 1;
}
}
/*
sin(a+b) = sin(a)*cos(b) + cos(a)*sin(b)
cos(a+b) = cos(a)*cos(b) - sin(a)*sin(b)
[F1+e1]*[F2+e2] + [F3+e3]*[F4+e4] - F1 F2 + F3 F4 =
e1 e2 + e3 e4 + e2 F1 + e1 F2 + e4 F3 + e3 F4
<= (e1 + e2 + e3 + e4) + 1 (ulp)
<= 2*left_err + 2*right_err + 1 (ulp)
Truncating both terms before adding adds another 2 ulp, so the error
is bounded by
<= 2*left_err + 2*right_err + 3 (ulp)
*/
if (p1 == 0 && p2 == 0) /* no table lookups */
{
sinptr = sina;
cosptr = cosa;
}
else if (p1 == 0 || p2 == 0) /* only one table lookup */
{
mp_srcptr sinc, cosc;
if (wp <= ARB_SIN_COS_TAB1_PREC) /* must be in table 1 */
{
sinc = arb_sin_cos_tab1[2 * p1] + ARB_SIN_COS_TAB1_LIMBS - wn;
cosc = arb_sin_cos_tab1[2 * p1 + 1] + ARB_SIN_COS_TAB1_LIMBS - wn;
}
else if (p1 != 0)
{
sinc = arb_sin_cos_tab21[2 * p1] + ARB_SIN_COS_TAB2_LIMBS - wn;
cosc = arb_sin_cos_tab21[2 * p1 + 1] + ARB_SIN_COS_TAB2_LIMBS - wn;
}
else
{
sinc = arb_sin_cos_tab22[2 * p2] + ARB_SIN_COS_TAB2_LIMBS - wn;
cosc = arb_sin_cos_tab22[2 * p2 + 1] + ARB_SIN_COS_TAB2_LIMBS - wn;
}
if ((want_sin && !swapsincos) || (want_cos && swapsincos))
{
mpn_mul_n(ta, sina, cosc, wn);
mpn_mul_n(tb, cosa, sinc, wn);
mpn_add_n(w, ta + wn, tb + wn, wn);
}
if ((want_cos && !swapsincos) || (want_sin && swapsincos))
{
mpn_mul_n(ta, cosa, cosc, wn);
mpn_mul_n(tb, sina, sinc, wn);
mpn_sub_n(ta, ta + wn, tb + wn, wn);
}
sinptr = w;
cosptr = ta;
error = 2 * error + 2 * 1 + 3;
}
else /* two table lookups, must be in table 2 */
{
mp_srcptr sinc, cosc, sind, cosd;
sinc = arb_sin_cos_tab21[2 * p1] + ARB_SIN_COS_TAB2_LIMBS - wn;
cosc = arb_sin_cos_tab21[2 * p1 + 1] + ARB_SIN_COS_TAB2_LIMBS - wn;
sind = arb_sin_cos_tab22[2 * p2] + ARB_SIN_COS_TAB2_LIMBS - wn;
cosd = arb_sin_cos_tab22[2 * p2 + 1] + ARB_SIN_COS_TAB2_LIMBS - wn;
mpn_mul_n(ta, sinc, cosd, wn);
mpn_mul_n(tb, cosc, sind, wn);
mpn_add_n(sinb, ta + wn, tb + wn, wn);
mpn_mul_n(ta, cosc, cosd, wn);
mpn_mul_n(tb, sinc, sind, wn);
mpn_sub_n(cosb, ta + wn, tb + wn, wn);
error2 = 2 * 1 + 2 * 1 + 3;
if ((want_sin && !swapsincos) || (want_cos && swapsincos))
{
mpn_mul_n(ta, sina, cosb, wn);
mpn_mul_n(tb, cosa, sinb, wn);
mpn_add_n(w, ta + wn, tb + wn, wn);
}
if ((want_cos && !swapsincos) || (want_sin && swapsincos))
{
mpn_mul_n(ta, cosa, cosb, wn);
mpn_mul_n(tb, sina, sinb, wn);
mpn_sub_n(ta, ta + wn, tb + wn, wn);
}
error = 2 * error + 2 * error2 + 3;
sinptr = w;
cosptr = ta;
}
/* PART 3: compute propagated error and write output */
if (swapsincos)
{
mp_ptr tmptr = sinptr;
sinptr = cosptr;
cosptr = tmptr;
}
/*
We have two sources of error.
1. Computation error:
error * 2^(-wprounded)
2. With input radius r != 0, the propagated error bound:
sin(x): min(2, r, |cos(x)|*r + 0.5*r^2)
cos(x): min(2, r, |sin(x)|*r + 0.5*r^2)
We skip the min by 2 since this is unnecessary with the
separate code for wide intervals.
*/
if (radman == 0)
{
if (want_sin)
{
mag_set_ui_2exp_si(arb_radref(zsin), error, -wprounded);
if (want_cos)
mag_set(arb_radref(zcos), arb_radref(zsin));
}
else
{
mag_set_ui_2exp_si(arb_radref(zcos), error, -wprounded);
}
}
else
{
mag_t sin_err, cos_err, quadratic, comp_err, xrad_copy;
mp_limb_t A_sin, A_cos, A_exp;
/* Copy xrad to support aliasing (note: the exponent has
also been clamped earlier). */
MAG_MAN(xrad_copy) = radman;
MAG_EXP(xrad_copy) = radexp;
/* Bound computed error. */
if (error != 0)
{
mag_init(comp_err); /* no need to free */
mag_set_ui_2exp_si(comp_err, error, -wprounded);
}
/* Bound quadratic term for propagated error: 0.5*r^2 */
mag_init(quadratic); /* no need to free */
mag_nonzero_fast_mul(quadratic, xrad_copy, xrad_copy);
MAG_EXP(quadratic) -= 1;
/* Bound linear term for propagated error: cos(x)*r, sin(x)*r. */
/* Note: we could have used the computed values, but then we would
need to incorporate the computed error which would be slightly
messier, and we would also need extra cases when only computing
one of the functions. */
A_cos = arb_sin_cos_tab1[2 * p1_tab1][ARB_SIN_COS_TAB1_LIMBS - 1];
A_sin = arb_sin_cos_tab1[2 * p1_tab1 + 1][ARB_SIN_COS_TAB1_LIMBS - 1];
/* Note: ARB_SIN_COS_TAB1_BITS == 8 */
/* Adding 2 ulps (here ulp = 2^-8) gives an upper bound.
The truncated table entry underestimates the sine or
cosine of x by at most 1 ulp, and the top bits of x
underestimate x by at most 1 ulp. */
A_sin = (A_sin >> (FLINT_BITS - ARB_SIN_COS_TAB1_BITS)) + 2;
A_cos = (A_cos >> (FLINT_BITS - ARB_SIN_COS_TAB1_BITS)) + 2;
A_exp = -ARB_SIN_COS_TAB1_BITS;
if (swapsincos)
{
mp_limb_t tt = A_sin;
A_sin = A_cos;
A_cos = tt;
}
A_sin *= ((MAG_MAN(xrad_copy) >> (MAG_BITS - ARB_SIN_COS_TAB1_BITS)) + LIMB_ONE);
A_cos *= ((MAG_MAN(xrad_copy) >> (MAG_BITS - ARB_SIN_COS_TAB1_BITS)) + LIMB_ONE);
A_exp -= ARB_SIN_COS_TAB1_BITS;
A_exp += radexp;
if (want_sin)
{
mag_init(sin_err);
mag_set_ui_2exp_si(sin_err, A_sin, A_exp);
mag_nonzero_fast_add(sin_err, sin_err, quadratic);
/* The propagated error is certainly at most r */
if (mag_nonzero_fast_cmp(sin_err, xrad_copy) > 0)
mag_fast_set(sin_err, xrad_copy);
/* Add the computed error. */
if (error != 0)
mag_nonzero_fast_add(sin_err, sin_err, comp_err);
/* Set it, and clear the original output variable which could
have a bignum exponent. */
mag_swap(arb_radref(zsin), sin_err);
mag_clear(sin_err);
}
/* The same as above. */
if (want_cos)
{
mag_init(cos_err);
mag_set_ui_2exp_si(cos_err, A_cos, A_exp);
mag_nonzero_fast_add(cos_err, cos_err, quadratic);
if (mag_nonzero_fast_cmp(cos_err, xrad_copy) > 0)
mag_fast_set(cos_err, xrad_copy);
if (error != 0)
mag_nonzero_fast_add(cos_err, cos_err, comp_err);
mag_swap(arb_radref(zcos), cos_err);
mag_clear(cos_err);
}
}
/* Set the midpoints. */
if (want_sin)
{
inexact = _arf_set_mpn_fixed(arb_midref(zsin), sinptr,
wn, wn, sinnegative, prec, ARB_RND);
if (inexact)
arf_mag_add_ulp(arb_radref(zsin),
arb_radref(zsin), arb_midref(zsin), prec);
}
if (want_cos)
{
inexact = _arf_set_mpn_fixed(arb_midref(zcos), cosptr,
wn, wn, cosnegative, prec, ARB_RND);
if (inexact)
arf_mag_add_ulp(arb_radref(zcos),
arb_radref(zcos), arb_midref(zcos), prec);
}
TMP_END;
}
void
arb_sin_cos(arb_t s, arb_t c, const arb_t x, slong prec)
{
_arb_sin_cos(s, c, arb_midref(x), arb_radref(x), prec);
}
void
arb_sin(arb_t s, const arb_t x, slong prec)
{
_arb_sin_cos(s, NULL, arb_midref(x), arb_radref(x), prec);
}
void
arb_cos(arb_t c, const arb_t x, slong prec)
{
_arb_sin_cos(NULL, c, arb_midref(x), arb_radref(x), prec);
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。