diff options
author | thevenyp <thevenyp@211d60ee-9f03-0410-a15a-8952a2c7a4e4> | 2008-03-27 14:10:38 +0000 |
---|---|---|
committer | thevenyp <thevenyp@211d60ee-9f03-0410-a15a-8952a2c7a4e4> | 2008-03-27 14:10:38 +0000 |
commit | e6cae66ca8ea855c7a3161dae81b64d0c1837f45 (patch) | |
tree | 4315356fb760c8faaf5f441a89b8bd7445c88d4b | |
parent | 03c9bdae2bb463e21e97c1f7a0c94ae1f47fb727 (diff) | |
download | mpc-e6cae66ca8ea855c7a3161dae81b64d0c1837f45.tar.gz |
Add new cosine function.
git-svn-id: svn://scm.gforge.inria.fr/svn/mpc/trunk@80 211d60ee-9f03-0410-a15a-8952a2c7a4e4
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | cos.c | 226 | ||||
-rw-r--r-- | mpc.h | 1 | ||||
-rw-r--r-- | tcos.c | 101 |
4 files changed, 330 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am index c11c285..620408c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,7 @@ include_HEADERS = mpc.h lib_LTLIBRARIES = libmpc.la libmpc_la_SOURCES = mpc-impl.h abs.c add.c add_fr.c add_ui.c clear.c \ -cmp.c cmp_si_si.c conj.c div_2exp.c div.c div_fr.c div_ui.c exp.c \ +cmp.c cmp_si_si.c conj.c cos.c div_2exp.c div.c div_fr.c div_ui.c exp.c \ get_prec2.c get_prec.c init2.c init3.c init.c inp_str.c mul_2exp.c mul.c \ mul_fr.c mul_i.c mul_si.c mul_ui.c neg.c norm.c out_str.c random2.c random.c \ set.c set_d_d.c set_dfl_prec.c set_prec.c set_si_si.c set_ui_fr.c \ @@ -13,7 +13,7 @@ ui_ui_sub.c LDADD = $(top_builddir)/libmpc.la check_INCLUDES = -I$(top_srcdir) -check_PROGRAMS = test tabs tdiv texp tmul tsin tsqr +check_PROGRAMS = test tabs tcos tdiv texp tmul tsin tsqr TESTS = $(check_PROGRAMS) CLEANFILES = $(top_builddir)/mpc_test @@ -0,0 +1,226 @@ +/* mpc_cos -- cosine of a complex number. + +Copyright (C) 2008 INRIA. + +This file is part of the MPC Library. + +The MPC 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 2.1 of the License, or (at your +option) any later version. + +The MPC 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 MPC Library; see the file COPYING.LIB. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +MA 02111-1307, USA. */ + +#include "mpfr.h" +#include "mpc.h" +#include "mpc-impl.h" + +void +mpc_cos (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + mpfr_t x, y, z; + mp_prec_t prec; + int ok = 0; + + /* 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)); + + return; + } + + if (mpfr_nan_p (MPC_IM (op))) + { + /* cos(-Inf + i * NaN) = NaN + i * Inf */ + /* cos(+Inf + i * NaN) = NaN - i * Inf */ + /* 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_number_p (MPC_RE (op))) + { + 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)); + } + else + mpfr_set (MPC_IM(rop), MPC_RE (op), MPC_RND_IM (rnd)); + + mpfr_set_nan (MPC_RE (rop)); + + return; + } + + if (mpfr_inf_p (MPC_RE (op))) + { + /* cos(-Inf - i * Inf) = cos(+Inf + i * 0) = +Inf + i * NaN */ + /* cos(-Inf + i * Inf) = cos(+Inf - i * 0) = +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), +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)); + + return; + } + + 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), + 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)); + + mpfr_clear (s); + mpfr_clear (c); + } + + return; + } + + 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))) + mpfr_set_ui (MPC_RE (rop), 1, MPC_RND_RE (rnd)); + else + 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; + } + + if (mpfr_zero_p (MPC_IM (op))) + { + /* cos(-x - i * 0) = cos(x + i * 0) = cos(x) - i * 0 + cos(-x + i * 0) = cos(x - i * 0) = cos(x) + i * 0, + when x > 0 */ + const int imag_sign = + mpfr_signbit (MPC_RE (op)) == mpfr_signbit (MPC_IM (op)); + + mpfr_setsign (MPC_IM (rop), MPC_IM (op), imag_sign, MPC_RND_IM (rnd)); + mpfr_cos (MPC_RE (rop), MPC_RE (op), MPC_RND_IM (rnd)); + + return; + } + + /* 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, MPC_RND_RE(rnd), + MPFR_PREC(MPC_RE(rop))); + if (ok) /* compute imaginary part */ + { + mpfr_sinh (z, MPC_IM(op), GMP_RNDN); + mpfr_mul (y, y, z, GMP_RNDN); + ok = mpfr_can_round (y, prec - 2, GMP_RNDN, MPC_RND_IM(rnd), + MPFR_PREC(MPC_IM(rop))); + } + } + while (ok == 0); + + mpfr_set (MPC_RE(rop), x, MPC_RND_RE(rnd)); + mpfr_set (MPC_IM(rop), y, MPC_RND_IM(rnd)); + + mpfr_clear (x); + mpfr_clear (y); + mpfr_clear (z); +} @@ -135,6 +135,7 @@ int mpc_cmp __MPC_PROTO ((mpc_srcptr, mpc_srcptr)); int mpc_cmp_si_si __MPC_PROTO ((mpc_srcptr, long int, long int)); void mpc_exp __MPC_PROTO ((mpc_ptr, mpc_srcptr, mpc_rnd_t)); void mpc_sin __MPC_PROTO ((mpc_ptr, mpc_srcptr, mpc_rnd_t)); +void mpc_cos __MPC_PROTO ((mpc_ptr, mpc_srcptr, mpc_rnd_t)); void mpc_clear __MPC_PROTO ((mpc_ptr)); void mpc_init __MPC_PROTO ((mpc_ptr)); void mpc_random __MPC_PROTO ((mpc_ptr)); @@ -0,0 +1,101 @@ +/* test file for mpc_cos. + +Copyright (C) 2007 INRIA. + +This file is part of the MPC Library. + +The MPC 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 2.1 of the License, or (at your +option) any later version. + +The MPC 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 MPC Library; see the file COPYING.LIB. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +MA 02111-1307, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include "gmp.h" +#include "mpfr.h" +#include "mpc.h" + + +int +main() +{ + mpc_t x, z, t, u; + mpfr_t f, g; + mp_prec_t prec; + + mpc_init (x); + mpc_init (z); + mpc_init (t); + mpc_init (u); + mpfr_init (g); + + for (prec = 2; prec <= 1000; prec++) + { + mpc_set_prec (x, prec); + mpc_set_prec (z, prec); + mpc_set_prec (t, prec); + mpc_set_prec (u, 4*prec); + mpfr_set_prec (g, prec); + + /* check that cos(I*b) = cosh(b) */ + mpfr_set_ui (MPC_RE (x), 0, GMP_RNDN); + mpfr_random (MPC_IM (x)); + + mpc_cos (z, x, MPC_RNDNN); + mpfr_cosh (g, MPC_IM(x), GMP_RNDN); + if (mpfr_cmp_ui (MPC_IM(z), 0) || mpfr_cmp (g, MPC_RE(z))) + { + fprintf (stderr, "Error in mpc_cos: cos(I*x) <> cosh(x)\n" + "got "); + mpc_out_str (stderr, 10, 0, z, MPC_RNDNN); + fprintf (stderr, "\nexpected "); + mpfr_set (MPC_RE (z), g, GMP_RNDN); + mpfr_set_ui (MPC_IM (z), 0, GMP_RNDN); + mpc_out_str (stderr, 10, 0, z, MPC_RNDNN); + fprintf (stderr, "\n"); + exit (1); + } + } + + + /* We also compute the result with four times the precision and check */ + /* whether the rounding is correct. Error reports in this part of the */ + /* algorithm might still be wrong, though, since there are two */ + /* consecutive roundings. */ + mpc_random (x); + mpc_cos (z, x, MPC_RNDNN); + mpc_cos (u, x, MPC_RNDNN); + mpc_set (t, u, MPC_RNDNN); + + if (mpc_cmp (z, t)) + { + fprintf (stderr, "rounding in cos might be incorrect for\nx="); + mpc_out_str (stderr, 2, 0, x, MPC_RNDNN); + fprintf (stderr, "\nmpc_cos gives "); + mpc_out_str (stderr, 2, 0, z, MPC_RNDNN); + fprintf (stderr, "\nmpc_cos quadruple precision gives "); + mpc_out_str (stderr, 2, 0, u, MPC_RNDNN); + fprintf (stderr, "\nand is rounded to "); + mpc_out_str (stderr, 2, 0, t, MPC_RNDNN); + fprintf (stderr, "\n"); + exit (1); + } + + mpc_clear (x); + mpc_clear (z); + mpc_clear (t); + mpc_clear (u); + mpfr_clear (g); + + return 0; +} |