summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorthevenyp <thevenyp@211d60ee-9f03-0410-a15a-8952a2c7a4e4>2008-03-27 14:10:38 +0000
committerthevenyp <thevenyp@211d60ee-9f03-0410-a15a-8952a2c7a4e4>2008-03-27 14:10:38 +0000
commite6cae66ca8ea855c7a3161dae81b64d0c1837f45 (patch)
tree4315356fb760c8faaf5f441a89b8bd7445c88d4b
parent03c9bdae2bb463e21e97c1f7a0c94ae1f47fb727 (diff)
downloadmpc-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.am4
-rw-r--r--cos.c226
-rw-r--r--mpc.h1
-rw-r--r--tcos.c101
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
diff --git a/cos.c b/cos.c
new file mode 100644
index 0000000..e13d6ca
--- /dev/null
+++ b/cos.c
@@ -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);
+}
diff --git a/mpc.h b/mpc.h
index 008e805..4caf9ac 100644
--- a/mpc.h
+++ b/mpc.h
@@ -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));
diff --git a/tcos.c b/tcos.c
new file mode 100644
index 0000000..0ff00fc
--- /dev/null
+++ b/tcos.c
@@ -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;
+}