diff options
author | zimmerma <zimmerma@280ebfd0-de03-0410-8827-d642c229c3f4> | 2020-12-16 10:02:47 +0000 |
---|---|---|
committer | zimmerma <zimmerma@280ebfd0-de03-0410-8827-d642c229c3f4> | 2020-12-16 10:02:47 +0000 |
commit | 85ddb25cb0d6c08210e3bee519db9d599d3d3ffe (patch) | |
tree | 65464dd6ff10f2b134ca81bb9718e4fba870761d | |
parent | b487cf60a5bacd76d0ad465972a9092e01cfe9e5 (diff) | |
download | mpfr-85ddb25cb0d6c08210e3bee519db9d599d3d3ffe.tar.gz |
added new function mpfr_tanu
git-svn-id: https://scm.gforge.inria.fr/anonscm/svn/mpfr/trunk@14210 280ebfd0-de03-0410-8827-d642c229c3f4
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | doc/mpfr.texi | 8 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/mpfr.h | 1 | ||||
-rw-r--r-- | src/tanu.c | 177 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/ttanu.c | 218 |
7 files changed, 404 insertions, 6 deletions
@@ -22,7 +22,7 @@ https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., Changes from versions 4.1.* to version 4.2.0: - The "" release. -- New functions mpfr_cosu and mpfr_sinu (experimental). +- New functions mpfr_cosu, mpfr_sinu and mpfr_tanu (experimental). - Bug fixes. In particular, for the formatted output functions (mpfr_printf, etc.), the case where the precision consists only of a period has been fixed diff --git a/doc/mpfr.texi b/doc/mpfr.texi index afe2a25f9..0afd01568 100644 --- a/doc/mpfr.texi +++ b/doc/mpfr.texi @@ -2269,9 +2269,10 @@ tangent of @var{op}, rounded in the direction @var{rnd}. @deftypefun int mpfr_cosu (mpfr_t @var{rop}, mpfr_t @var{op}, unsigned long @var{u}, mpfr_rnd_t @var{rnd}) @deftypefunx int mpfr_sinu (mpfr_t @var{rop}, mpfr_t @var{op}, unsigned long @var{u}, mpfr_rnd_t @var{rnd}) -Set @var{rop} to the cosine (resp. sine) of @m{@var{op} \times 2\pi/u,@var{op} multiplied -by 2@tie{}Pi and divided by @var{u}}. Thus if @var{u} equals 360, one -gets the cosine (resp. sine) for @var{op} in degrees. +@deftypefunx int mpfr_tanu (mpfr_t @var{rop}, mpfr_t @var{op}, unsigned long @var{u}, mpfr_rnd_t @var{rnd}) +Set @var{rop} to the cosine (resp. sine and tangent) of @m{@var{op} \times 2\pi/u,@var{op} multiplied +by 2@tie{}Pi and divided by @var{u}}. For example, if @var{u} equals 360, one +gets the cosine (resp. sine and tangent) for @var{op} in degrees. For @code{mpfr_cosu}, when @m{@var{op} \times 2/u,@var{op} multiplied by 2 and divided by @var{u}} is a half-integer, the result is +0, @@ -2282,6 +2283,7 @@ when @m{@var{op} \times 2/u,@var{op} multiplied by 2 and divided by @var{u}} is an integer, the result is zero with the same sign as @var{op}, following IEEE@tie{}754-2019 (sinPi), so that the function is odd. +Similarly, the function @code{mpfr_tanu} follows IEEE@tie{}754-2019 (tanPi). Note: these functions are experimental and their interface might change in future versions. diff --git a/src/Makefile.am b/src/Makefile.am index 10686c5f7..c1cd06a4b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -67,7 +67,7 @@ grandom.c fpif.c set_float128.c get_float128.c rndna.c nrandom.c \ random_deviate.h random_deviate.c erandom.c mpfr-mini-gmp.c \ mpfr-mini-gmp.h fmma.c log_ui.c gamma_inc.c ubf.c invert_limb.h \ invsqrt_limb.h beta.c odd_p.c get_q.c pool.c total_order.c set_d128.c \ -get_d128.c nbits_ulong.c cmpabs_ui.c sinu.c cosu.c +get_d128.c nbits_ulong.c cmpabs_ui.c sinu.c cosu.c tanu.c nodist_libmpfr_la_SOURCES = $(BUILT_SOURCES) diff --git a/src/mpfr.h b/src/mpfr.h index 74d6c675a..ed63df92f 100644 --- a/src/mpfr.h +++ b/src/mpfr.h @@ -723,6 +723,7 @@ __MPFR_DECLSPEC int mpfr_cot (mpfr_ptr, mpfr_srcptr, mpfr_rnd_t); __MPFR_DECLSPEC int mpfr_sinu (mpfr_ptr, mpfr_srcptr, unsigned long, mpfr_rnd_t); __MPFR_DECLSPEC int mpfr_cosu (mpfr_ptr, mpfr_srcptr, unsigned long, mpfr_rnd_t); +__MPFR_DECLSPEC int mpfr_tanu (mpfr_ptr, mpfr_srcptr, unsigned long, mpfr_rnd_t); __MPFR_DECLSPEC int mpfr_hypot (mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_rnd_t); __MPFR_DECLSPEC int mpfr_erf (mpfr_ptr, mpfr_srcptr, mpfr_rnd_t); diff --git a/src/tanu.c b/src/tanu.c new file mode 100644 index 000000000..1d9710a8b --- /dev/null +++ b/src/tanu.c @@ -0,0 +1,177 @@ +/* mpfr_tanu -- tanu(x) = tan(2*pi*x/u) + +Copyright 2020 Free Software Foundation, Inc. +Contributed by the AriC and Caramba projects, INRIA. + +This file is part of the GNU MPFR Library. + +The GNU MPFR Library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +The GNU MPFR Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see +https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#define MPFR_NEED_LONGLONG_H +#include "mpfr-impl.h" + +/* FIXME[VL]: Implement the range reduction in this function. + That's the whole point of tanu compared to tan. */ + +/* put in y the corrected-rounded value of tan(2*pi*x/u) */ +int +mpfr_tanu (mpfr_ptr y, mpfr_srcptr x, unsigned long u, mpfr_rnd_t rnd_mode) +{ + mpfr_prec_t precy, prec; + mpfr_exp_t expx, expt, err; + mpfr_t t; + int inexact = 0, nloops = 0, underflow = 0; + MPFR_ZIV_DECL (loop); + MPFR_SAVE_EXPO_DECL (expo); + + 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 (u == 0 || MPFR_UNLIKELY (MPFR_IS_SINGULAR (x))) + { + /* for u=0, return NaN */ + if (u == 0 || 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); + } + } + + MPFR_SAVE_EXPO_MARK (expo); + + precy = MPFR_PREC (y); + expx = MPFR_GET_EXP (x); + /* for x large, since argument reduction is expensive, we want to avoid + any failure in Ziv's strategy, thus we take into account expx too */ + prec = precy + MPFR_INT_CEIL_LOG2 (MAX(precy,expx)) + 8; + MPFR_ASSERTD(prec >= 2); + mpfr_init2 (t, prec); + MPFR_ZIV_INIT (loop, prec); + for (;;) + { + int inex; + nloops ++; + /* We first compute an approximation t of 2*pi*x/u, then call tan(t). + If t = 2*pi*x/u + s, then + |tan(t) - tan(2*pi*x/u)| = |s| * (1 + tan(v)^2) where v is in the + interval [t, t+s]. If we ensure that |t| >= |2*pi*x/u|, since tan() is + increasing, we can bound tan(v)^2 by tan(t)^2. */ + mpfr_set_prec (t, prec); + mpfr_const_pi (t, MPFR_RNDU); /* t = pi * (1 + theta1) where + |theta1| <= 2^(1-prec) */ + mpfr_mul_2ui (t, t, 1, MPFR_RNDN); /* t = 2*pi * (1 + theta1) */ + mpfr_mul (t, t, x, MPFR_RNDA); /* t = 2*pi*x * (1 + theta2)^2 where + |theta2| <= 2^(1-prec) */ + inex = mpfr_div_ui (t, t, u, MPFR_RNDN); + /* t = 2*pi*x/u * (1 + theta3)^3 where |theta3| <= 2^(1-prec) */ + /* if t is zero here, it means the division by u underflows, then + tan(t) also underflows, since |tan(x)| <= |x|. */ + if (MPFR_UNLIKELY (MPFR_IS_ZERO (t))) + { + inexact = mpfr_underflow (y, rnd_mode, MPFR_SIGN(t)); + MPFR_SAVE_EXPO_UPDATE_FLAGS (expo, MPFR_FLAGS_INEXACT + | MPFR_FLAGS_UNDERFLOW); + underflow = 1; + goto end; + } + /* emulate mpfr_div_ui (t, t, u, MPFR_RNDA) above, so that t is rounded + away from zero */ + if (MPFR_SIGN(t) > 0 && inex < 0) + mpfr_nextabove (t); + else if (MPFR_SIGN(t) < 0 && inex > 0) + mpfr_nextbelow (t); + expt = MPFR_GET_EXP (t); + /* since prec >= 3, |(1 + theta3)^3 - 1| <= 4*theta3 <= 2^(3-prec) + thus |s| = |t - 2*pi*x/u| <= |t| * 2^(3-prec) */ + mpfr_tan (t, t, MPFR_RNDA); + { + /* compute an upper bound for 1+tan(t)^2 */ + mpfr_t z; + mpfr_init2 (z, 64); + mpfr_sqr (z, t, MPFR_RNDU); + mpfr_add_ui (z, z, 1, MPFR_RNDU); + expt += MPFR_GET_EXP (z); + /* now |t - tan(2*pi*x/u)| <= ulp(t) + 2^(expt + 3 - prec) */ + mpfr_clear (z); + } + /* t cannot be zero here, since we excluded t=0 before, which is the + only exact case where tan(t)=0, and we round away from zero */ + err = expt + 3 - prec; + expt = MPFR_GET_EXP (t); /* new exponent of t */ + /* the total error is bounded by 2^err + ulp(t) = 2^err + 2^(expt-prec) + thus if err <= expt-prec, it is bounded by 2^(expt-prec+1), + otherwise it is bounded by 2^(err+1). */ + err = (err <= expt - prec) ? expt - prec + 1 : err + 1; + /* normalize err for mpfr_can_round */ + err = expt - err; + if (MPFR_CAN_ROUND (t, err, precy, rnd_mode)) + break; + /* Check exact cases only after the first level of Ziv' strategy, to + avoid slowing down the average case. Exact cases are when 2*pi*x/u + is a multiple of pi/4, i.e., x/u a multiple of 1/8: + (a) x/u = {0,1/2} mod 1: return +0 or -0 + (b) x/u = {1/4,3/4} mod 1: return +Inf or -Inf + (c) x/u = {1/8,3/8,5/8,7/8} mod 1: return 1 or -1 */ + if (nloops == 1) + { + inexact = mpfr_div_ui (t, x, u, MPFR_RNDA); + mpfr_mul_2ui (t, t, 3, MPFR_RNDA); + if (inexact == 0 && mpfr_integer_p (t)) + { + mpz_t z; + unsigned long mod8; + mpz_init (z); + inexact = mpfr_get_z (z, t, MPFR_RNDZ); + MPFR_ASSERTN(inexact == 0); + mod8 = mpz_fdiv_ui (z, 8); + mpz_clear (z); + if (mod8 == 0 || mod8 == 4) /* case (a) */ + mpfr_set_zero (y, ((mod8 == 0) ? +1 : -1) * MPFR_SIGN (x)); + else if (mod8 == 2 || mod8 == 6) /* case (b) */ + { + mpfr_set_inf (y, (mod8 == 2) ? +1 : -1); + MPFR_SAVE_EXPO_UPDATE_FLAGS (expo, MPFR_FLAGS_DIVBY0); + } + else /* case (c) */ + { + if (mod8 == 1 || mod8 == 5) + mpfr_set_ui (y, 1, rnd_mode); + else + mpfr_set_si (y, -1, rnd_mode); + } + goto end; + } + } + MPFR_ZIV_NEXT (loop, prec); + } + MPFR_ZIV_FREE (loop); + + inexact = mpfr_set (y, t, rnd_mode); + + end: + mpfr_clear (t); + MPFR_SAVE_EXPO_FREE (expo); + return underflow ? inexact : mpfr_check_range (y, inexact, rnd_mode); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 190caaf13..2a4f16fa0 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -50,7 +50,7 @@ TESTS_NO_TVERSION = tabort_prec_max tassert tabort_defalloc1 \ tset_str tset_z tset_z_2exp tsi_op tsin tsin_cos tsinh tsinh_cosh \ tsinu tsprintf tsqr tsqrt tsqrt_ui tstckintc tstdint tstrtofr \ tsub tsub1sp tsub_d tsub_ui tsubnormal tsum tswap ttan ttanh \ - ttotal_order ttrunc tui_div tui_pow tui_sub turandom tvalist \ + ttanu ttotal_order ttrunc tui_div tui_pow tui_sub turandom tvalist \ ty0 ty1 tyn tzeta tzeta_ui check_PROGRAMS = tversion $(TESTS_NO_TVERSION) diff --git a/tests/ttanu.c b/tests/ttanu.c new file mode 100644 index 000000000..9933a0a26 --- /dev/null +++ b/tests/ttanu.c @@ -0,0 +1,218 @@ +/* Test file for mpfr_tanu. + +Copyright 2020 Free Software Foundation, Inc. +Contributed by the AriC and Caramba projects, INRIA. + +This file is part of the GNU MPFR Library. + +The GNU MPFR Library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +The GNU MPFR Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see +https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include "mpfr-test.h" + +static void +test_singular (void) +{ + mpfr_t x, y; + int inexact; + + mpfr_init (x); + mpfr_init (y); + + /* check u = 0 */ + mpfr_set_ui (x, 17, MPFR_RNDN); + inexact = mpfr_tanu (y, x, 0, MPFR_RNDN); + MPFR_ASSERTN(mpfr_nan_p (y)); + + /* check x = NaN */ + mpfr_set_nan (x); + inexact = mpfr_tanu (y, x, 1, MPFR_RNDN); + MPFR_ASSERTN(mpfr_nan_p (y)); + + /* check x = +Inf */ + mpfr_set_inf (x, 1); + inexact = mpfr_tanu (y, x, 1, MPFR_RNDN); + MPFR_ASSERTN(mpfr_nan_p (y)); + + /* check x = -Inf */ + mpfr_set_inf (x, -1); + inexact = mpfr_tanu (y, x, 1, MPFR_RNDN); + MPFR_ASSERTN(mpfr_nan_p (y)); + + /* check x = +0 */ + mpfr_set_zero (x, 1); + inexact = mpfr_tanu (y, x, 1, MPFR_RNDN); + MPFR_ASSERTN(mpfr_zero_p (y) && mpfr_signbit (y) == 0); + MPFR_ASSERTN(inexact == 0); + + /* check x = -0 */ + mpfr_set_zero (x, -1); + inexact = mpfr_tanu (y, x, 1, MPFR_RNDN); + MPFR_ASSERTN(mpfr_zero_p (y) && mpfr_signbit (y) != 0); + MPFR_ASSERTN(inexact == 0); + + mpfr_clear (x); + mpfr_clear (y); +} + +static void +test_exact (void) +{ + mpfr_t x, y; + int inexact; + + mpfr_init2 (x, 6); + mpfr_init2 (y, 6); + + /* check n + 0.5 for n integer */ + for (int n = 0; n < 10; n++) + { + /* check 2n+0.5 for n>=0: +Inf and divide by 0 exception */ + mpfr_set_ui (x, 4 * n + 1, MPFR_RNDN); + mpfr_clear_divby0 (); + inexact = mpfr_tanu (y, x, 4, MPFR_RNDN); + MPFR_ASSERTN(mpfr_inf_p (y) && mpfr_sgn (y) > 0); + MPFR_ASSERTN(inexact == 0); + MPFR_ASSERTN(mpfr_divby0_p ()); + + /* check 2n+1 for n>=0: -0 */ + mpfr_set_ui (x, 4 * n + 2, MPFR_RNDN); + mpfr_clear_divby0 (); + inexact = mpfr_tanu (y, x, 4, MPFR_RNDN); + MPFR_ASSERTN(mpfr_zero_p (y) && mpfr_signbit (y) != 0); + MPFR_ASSERTN(inexact == 0); + MPFR_ASSERTN(!mpfr_divby0_p ()); + + /* check 2n+1.5 for n>=0: -Inf and divide by 0 exception */ + mpfr_set_ui (x, 4 * n + 3, MPFR_RNDN); + mpfr_clear_divby0 (); + inexact = mpfr_tanu (y, x, 4, MPFR_RNDN); + MPFR_ASSERTN(mpfr_inf_p (y) && mpfr_sgn (y) < 0); + MPFR_ASSERTN(inexact == 0); + MPFR_ASSERTN(mpfr_divby0_p ()); + + /* check 2n+2 for n>=0: +0 */ + mpfr_set_ui (x, 4 * n + 4, MPFR_RNDN); + mpfr_clear_divby0 (); + inexact = mpfr_tanu (y, x, 4, MPFR_RNDN); + MPFR_ASSERTN(mpfr_zero_p (y) && mpfr_signbit (y) == 0); + MPFR_ASSERTN(inexact == 0); + MPFR_ASSERTN(!mpfr_divby0_p ()); + + /* check -2n-0.5 for n>=0: -Inf and divide by 0 exception */ + mpfr_set_si (x, -4 * n - 1, MPFR_RNDN); + mpfr_clear_divby0 (); + inexact = mpfr_tanu (y, x, 4, MPFR_RNDN); + MPFR_ASSERTN(mpfr_inf_p (y) && mpfr_sgn (y) < 0); + MPFR_ASSERTN(inexact == 0); + MPFR_ASSERTN(mpfr_divby0_p ()); + + /* check -2n-1 for n>=0: +0 */ + mpfr_set_si (x, -4 * n - 2, MPFR_RNDN); + mpfr_clear_divby0 (); + inexact = mpfr_tanu (y, x, 4, MPFR_RNDN); + MPFR_ASSERTN(mpfr_zero_p (y) && mpfr_signbit (y) == 0); + MPFR_ASSERTN(inexact == 0); + MPFR_ASSERTN(!mpfr_divby0_p ()); + + /* check -2n-1.5 for n>=0: +Inf and divide by 0 exception */ + mpfr_set_si (x, -4 * n - 3, MPFR_RNDN); + mpfr_clear_divby0 (); + inexact = mpfr_tanu (y, x, 4, MPFR_RNDN); + MPFR_ASSERTN(mpfr_inf_p (y) && mpfr_sgn (y) > 0); + MPFR_ASSERTN(inexact == 0); + MPFR_ASSERTN(mpfr_divby0_p ()); + + /* check -2n-2 for n>=0: -0 */ + mpfr_set_si (x, -4 * n - 4, MPFR_RNDN); + mpfr_clear_divby0 (); + inexact = mpfr_tanu (y, x, 4, MPFR_RNDN); + MPFR_ASSERTN(mpfr_zero_p (y) && mpfr_signbit (y) != 0); + MPFR_ASSERTN(inexact == 0); + MPFR_ASSERTN(!mpfr_divby0_p ()); + } + + /* check 2*pi*x/u = pi/4 thus x/u = 1/8, for example x=1 and u=8 */ + mpfr_set_ui (x, 1, MPFR_RNDN); + inexact = mpfr_tanu (y, x, 8, MPFR_RNDN); + MPFR_ASSERTN(mpfr_cmp_ui (y, 1) == 0 && inexact == 0); + + /* check 2*pi*x/u = 3*pi/4 thus x/u = 3/8, for example x=3 and u=8 */ + mpfr_set_ui (x, 3, MPFR_RNDN); + inexact = mpfr_tanu (y, x, 8, MPFR_RNDN); + MPFR_ASSERTN(mpfr_cmp_si (y, -1) == 0 && inexact == 0); + + /* check 2*pi*x/u = 5*pi/4 thus x/u = 5/8, for example x=5 and u=8 */ + mpfr_set_ui (x, 5, MPFR_RNDN); + inexact = mpfr_tanu (y, x, 8, MPFR_RNDN); + MPFR_ASSERTN(mpfr_cmp_ui (y, 1) == 0 && inexact == 0); + + /* check 2*pi*x/u = 7*pi/4 thus x/u = 7/8, for example x=7 and u=8 */ + mpfr_set_ui (x, 7, MPFR_RNDN); + inexact = mpfr_tanu (y, x, 8, MPFR_RNDN); + MPFR_ASSERTN(mpfr_cmp_si (y, -1) == 0 && inexact == 0); + + mpfr_clear (x); + mpfr_clear (y); +} + +static void +test_regular (void) +{ + mpfr_t x, y, z; + int inexact; + + mpfr_init2 (x, 53); + mpfr_init2 (y, 53); + mpfr_init2 (z, 53); + + mpfr_set_ui (x, 17, MPFR_RNDN); + inexact = mpfr_tanu (y, x, 42, MPFR_RNDN); + /* y should be tan(2*17*pi/42) rounded to nearest */ + mpfr_set_str (z, "-0xa.e89b03074638p-4", 16, MPFR_RNDN); + MPFR_ASSERTN(mpfr_equal_p (y, z)); + MPFR_ASSERTN(inexact > 0); + + mpfr_clear (x); + mpfr_clear (y); + mpfr_clear (z); +} + +/* FIXME[VL]: For mpfr_tanu, the range reduction should not be expensive. + If I'm not mistaken, this is linear in the bitsize of the exponent + since one just needs to compute the argument modulo the integer u. */ +#define TEST_FUNCTION mpfr_tanu +#define ULONG_ARG2 +#ifndef MPFR_USE_MINI_GMP +#define REDUCE_EMAX 262143 /* otherwise arg. reduction is too expensive */ +#else +#define REDUCE_EMAX 16383 /* reduce further since mini-gmp works in O(n^2) */ +#endif +#include "tgeneric.c" + +int +main (void) +{ + tests_start_mpfr (); + + test_singular (); + test_exact (); + test_regular (); + + test_generic (MPFR_PREC_MIN, 100, 1); + + tests_end_mpfr (); + return 0; +} |