xref: /netbsd-src/external/lgpl3/mpfr/dist/src/sin.c (revision 73d56d5b0be8704e4f0a7e8221a2c7309572c9a1)
1 /* mpfr_sin -- sine of a floating-point number
2 
3 Copyright 2001-2020 Free Software Foundation, Inc.
4 Contributed by the AriC and Caramba projects, INRIA.
5 
6 This file is part of the GNU MPFR Library.
7 
8 The GNU MPFR Library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 The GNU MPFR Library is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16 License for more details.
17 
18 You should have received a copy of the GNU Lesser General Public License
19 along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
20 https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
21 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
22 
23 #define MPFR_NEED_LONGLONG_H
24 #include "mpfr-impl.h"
25 
26 static int
27 mpfr_sin_fast (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode)
28 {
29   int inex;
30 
31   inex = mpfr_sincos_fast (y, NULL, x, rnd_mode);
32   inex = inex & 3; /* 0: exact, 1: rounded up, 2: rounded down */
33   return (inex == 2) ? -1 : inex;
34 }
35 
36 int
37 mpfr_sin (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode)
38 {
39   mpfr_t c, xr;
40   mpfr_srcptr xx;
41   mpfr_exp_t expx, err1, err;
42   mpfr_prec_t precy, m;
43   int inexact, sign, reduce;
44   MPFR_ZIV_DECL (loop);
45   MPFR_SAVE_EXPO_DECL (expo);
46 
47   MPFR_LOG_FUNC
48     (("x[%Pu]=%.*Rg rnd=%d", mpfr_get_prec (x), mpfr_log_prec, x, rnd_mode),
49      ("y[%Pu]=%.*Rg inexact=%d", mpfr_get_prec (y), mpfr_log_prec, y,
50       inexact));
51 
52   if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x)))
53     {
54       if (MPFR_IS_NAN (x) || MPFR_IS_INF (x))
55         {
56           MPFR_SET_NAN (y);
57           MPFR_RET_NAN;
58         }
59       else /* x is zero */
60         {
61           MPFR_ASSERTD (MPFR_IS_ZERO (x));
62           MPFR_SET_ZERO (y);
63           MPFR_SET_SAME_SIGN (y, x);
64           MPFR_RET (0);
65         }
66     }
67 
68   expx = MPFR_GET_EXP (x);
69   err1 = -2 * expx;
70 
71   /* sin(x) = x - x^3/6 + ... so the error is < 2^(3*EXP(x)-2) */
72   MPFR_FAST_COMPUTE_IF_SMALL_INPUT (y, x, err1, 2, 0, rnd_mode, {});
73 
74   MPFR_SAVE_EXPO_MARK (expo);
75 
76   /* Compute initial precision */
77   precy = MPFR_PREC (y);
78 
79   if (precy >= MPFR_SINCOS_THRESHOLD)
80     {
81       inexact = mpfr_sin_fast (y, x, rnd_mode);
82       goto end;
83     }
84 
85   /* for x large, since argument reduction is expensive, we want to avoid
86      any failure in Ziv's strategy, thus we take into account expx too */
87   m = precy + MPFR_INT_CEIL_LOG2 (MAX(precy,expx)) + 8;
88 
89   /* since we compute sin(x) as sqrt(1-cos(x)^2), and for x small we have
90      cos(x)^2 ~ 1 - x^2, when subtracting cos(x)^2 from 1 we will lose
91      about -2*expx bits if expx < 0 */
92   if (expx < 0)
93     {
94       /* The following assertion includes a check for integer overflow.
95          At this point, precy < MPFR_SINCOS_THRESHOLD, so that both m and
96          err1 should be small enough. But the assertion makes the code
97          safer (a smart compiler might be able to remove it). */
98       MPFR_ASSERTN (err1 <= MPFR_PREC_MAX - m);
99       m += err1;
100     }
101 
102   mpfr_init (c);
103   mpfr_init (xr);
104 
105   MPFR_ZIV_INIT (loop, m);
106   for (;;)
107     {
108       /* first perform argument reduction modulo 2*Pi (if needed),
109          also helps to determine the sign of sin(x) */
110       if (expx >= 2) /* If Pi < x < 4, we need to reduce too, to determine
111                         the sign of sin(x). For 2 <= |x| < Pi, we could avoid
112                         the reduction. */
113         {
114           reduce = 1;
115           /* As expx + m - 1 will silently be converted into mpfr_prec_t
116              in the mpfr_set_prec call, the assert below may be useful to
117              avoid undefined behavior. */
118           MPFR_ASSERTN (expx + m - 1 <= MPFR_PREC_MAX);
119           mpfr_set_prec (c, expx + m - 1);
120           mpfr_set_prec (xr, m);
121           mpfr_const_pi (c, MPFR_RNDN);
122           mpfr_mul_2ui (c, c, 1, MPFR_RNDN);
123           mpfr_remainder (xr, x, c, MPFR_RNDN);
124           /* The analysis is similar to that of cos.c:
125              |xr - x - 2kPi| <= 2^(2-m). Thus we can decide the sign
126              of sin(x) if xr is at distance at least 2^(2-m) of both
127              0 and +/-Pi. */
128           mpfr_div_2ui (c, c, 1, MPFR_RNDN);
129           /* Since c approximates Pi with an error <= 2^(2-expx-m) <= 2^(-m),
130              it suffices to check that c - |xr| >= 2^(2-m). */
131           if (MPFR_IS_POS (xr))
132             mpfr_sub (c, c, xr, MPFR_RNDZ);
133           else
134             mpfr_add (c, c, xr, MPFR_RNDZ);
135           if (MPFR_IS_ZERO(xr)
136               || MPFR_GET_EXP(xr) < (mpfr_exp_t) 3 - (mpfr_exp_t) m
137               || MPFR_IS_ZERO(c)
138               || MPFR_GET_EXP(c) < (mpfr_exp_t) 3 - (mpfr_exp_t) m)
139             goto ziv_next;
140 
141           /* |xr - x - 2kPi| <= 2^(2-m), thus |sin(xr) - sin(x)| <= 2^(2-m) */
142           xx = xr;
143         }
144       else /* the input argument is already reduced */
145         {
146           reduce = 0;
147           xx = x;
148         }
149 
150       sign = MPFR_SIGN(xx);
151       /* now that the argument is reduced, precision m is enough */
152       mpfr_set_prec (c, m);
153       mpfr_cos (c, xx, MPFR_RNDA);    /* c = cos(x) rounded away */
154       mpfr_sqr (c, c, MPFR_RNDU);     /* away */
155       mpfr_ui_sub (c, 1, c, MPFR_RNDZ);
156       mpfr_sqrt (c, c, MPFR_RNDZ);
157       if (MPFR_IS_NEG_SIGN(sign))
158         MPFR_CHANGE_SIGN(c);
159 
160       /* Warning: c may be 0! */
161       if (MPFR_UNLIKELY (MPFR_IS_ZERO (c)))
162         {
163           /* Huge cancellation: increase prec a lot! */
164           m = MAX (m, MPFR_PREC (x));
165           m = 2 * m;
166         }
167       else
168         {
169           /* the absolute error on c is at most 2^(3-m-EXP(c)),
170              plus 2^(2-m) if there was an argument reduction.
171              Since EXP(c) <= 1, 3-m-EXP(c) >= 2-m, thus the error
172              is at most 2^(3-m-EXP(c)) in case of argument reduction. */
173           err = 2 * MPFR_GET_EXP (c) + (mpfr_exp_t) m - 3 - (reduce != 0);
174           if (MPFR_CAN_ROUND (c, err, precy, rnd_mode))
175             break;
176 
177           /* check for huge cancellation (Near 0) */
178           if (err < (mpfr_exp_t) MPFR_PREC (y))
179             m += MPFR_PREC (y) - err;
180           /* Check if near 1 */
181           if (MPFR_GET_EXP (c) == 1)
182             m += m;
183         }
184 
185     ziv_next:
186       /* Else generic increase */
187       MPFR_ZIV_NEXT (loop, m);
188     }
189   MPFR_ZIV_FREE (loop);
190 
191   inexact = mpfr_set (y, c, rnd_mode);
192   /* inexact cannot be 0, since this would mean that c was representable
193      within the target precision, but in that case mpfr_can_round will fail */
194 
195   mpfr_clear (c);
196   mpfr_clear (xr);
197 
198  end:
199   MPFR_SAVE_EXPO_FREE (expo);
200   return mpfr_check_range (y, inexact, rnd_mode);
201 }
202