xref: /netbsd-src/external/lgpl3/mpfr/dist/src/jyn_asympt.c (revision ba125506a622fe649968631a56eba5d42ff57863)
1 /* mpfr_jn_asympt, mpfr_yn_asympt -- shared code for mpfr_jn and mpfr_yn
2 
3 Copyright 2007-2023 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 #ifdef MPFR_JN
24 # define FUNCTION mpfr_jn_asympt
25 #else
26 # ifdef MPFR_YN
27 #  define FUNCTION mpfr_yn_asympt
28 # else
29 #  error "neither MPFR_JN nor MPFR_YN is defined"
30 # endif
31 #endif
32 
33 /* Implements asymptotic expansion for jn or yn (formulae 9.2.5 and 9.2.6
34    from Abramowitz & Stegun).
35    Assumes |z| > p log(2)/2, where p is the target precision
36    (z can be negative only for jn).
37    Return 0 if the expansion does not converge enough (the value 0 as inexact
38    flag should not happen for normal input).
39    Note: for MPFR_RNDF, it returns 0 if the expansion failed, and a non-zero
40    value otherwise (with no other meaning).
41 */
42 static int
FUNCTION(mpfr_ptr res,long n,mpfr_srcptr z,mpfr_rnd_t r)43 FUNCTION (mpfr_ptr res, long n, mpfr_srcptr z, mpfr_rnd_t r)
44 {
45   mpfr_t s, c, P, Q, t, iz, err_t, err_s, err_u;
46   mpfr_prec_t w;
47   long k;
48   int inex, stop, diverge = 0;
49   mpfr_exp_t err2, err;
50   MPFR_ZIV_DECL (loop);
51 
52   mpfr_init2 (c, 64);
53 
54   /* The terms of the asymptotic expansion grow like mu^(2k)/(8z)^(2k), where
55      mu = 4n^2, thus we need mu < 8|z| so that it converges,
56      i.e., n^2/2 < |z| */
57   MPFR_ASSERTD (n >= 0);
58   mpfr_set_ui (c, n, MPFR_RNDU);
59   mpfr_mul_ui (c, c, n, MPFR_RNDU);
60   mpfr_div_2ui (c, c, 1, MPFR_RNDU);
61   if (mpfr_cmpabs (c, z) >= 0)
62     {
63       mpfr_clear (c);
64       return 0; /* asymptotic expansion failed */
65     }
66 
67   w = MPFR_PREC(res) + MPFR_INT_CEIL_LOG2(MPFR_PREC(res)) + 4;
68 
69   MPFR_ZIV_INIT (loop, w);
70   for (;;)
71     {
72       int ok = 0;
73 
74       mpfr_set_prec (c, w);
75       mpfr_init2 (s, w);
76       mpfr_init2 (P, w);
77       mpfr_init2 (Q, w);
78       mpfr_init2 (t, w);
79       mpfr_init2 (iz, w);
80       mpfr_init2 (err_t, 31);
81       mpfr_init2 (err_s, 31);
82       mpfr_init2 (err_u, 31);
83 
84       /* Approximate sin(z) and cos(z). In the following, err <= k means that
85          the approximate value y and the true value x are related by
86          y = x * (1 + u)^k with |u| <= 2^(-w), following Higham's method. */
87       mpfr_sin_cos (s, c, z, MPFR_RNDN);
88       if (MPFR_IS_NEG(z))
89         mpfr_neg (s, s, MPFR_RNDN); /* compute jn/yn(|z|), fix sign later */
90       /* The absolute error on s/c is bounded by 1/2 ulp(1/2) <= 2^(-w-1). */
91       mpfr_add (t, s, c, MPFR_RNDN);
92       mpfr_sub (c, s, c, MPFR_RNDN);
93       mpfr_swap (s, t);
94       /* now s approximates sin(z)+cos(z), and c approximates sin(z)-cos(z),
95          with total absolute error bounded by 2^(1-w). */
96 
97       /* if s or c is zero, MPFR_GET_EXP will fail below */
98       if (MPFR_IS_ZERO(s) || MPFR_IS_ZERO(c))
99         goto clear; /* with ok=0 */
100 
101       /* precompute 1/(8|z|) */
102       mpfr_si_div (iz, MPFR_IS_POS(z) ? 1 : -1, z, MPFR_RNDN);   /* err <= 1 */
103       mpfr_div_2ui (iz, iz, 3, MPFR_RNDN);
104 
105       /* compute P and Q */
106       mpfr_set_ui (P, 1, MPFR_RNDN);
107       mpfr_set_ui (Q, 0, MPFR_RNDN);
108       mpfr_set_ui (t, 1, MPFR_RNDN); /* current term */
109       mpfr_set_ui (err_t, 0, MPFR_RNDN); /* error on t */
110       mpfr_set_ui (err_s, 0, MPFR_RNDN); /* error on P and Q (sum of errors) */
111       for (k = 1, stop = 0; stop < 4; k++)
112         {
113           /* compute next term: t(k)/t(k-1) = (2n+2k-1)(2n-2k+1)/(8kz) */
114           MPFR_LOG_MSG (("loop (k,stop) = (%ld,%d)\n", k, stop));
115           mpfr_mul_si (t, t, 2 * (n + k) - 1, MPFR_RNDN); /* err <= err_k + 1 */
116           mpfr_mul_si (t, t, 2 * (n - k) + 1, MPFR_RNDN); /* err <= err_k + 2 */
117           mpfr_div_ui (t, t, k, MPFR_RNDN);               /* err <= err_k + 3 */
118           mpfr_mul (t, t, iz, MPFR_RNDN);                 /* err <= err_k + 5 */
119           /* the relative error on t is bounded by (1+u)^(5k)-1, which is
120              bounded by 6ku for 6ku <= 0.02: first |5 log(1+u)| <= |5.5u|
121              for |u| <= 0.15, then |exp(5.5u)-1| <= 6u for |u| <= 0.02. */
122           mpfr_mul_ui (err_t, t, 6 * k, MPFR_IS_POS(t) ? MPFR_RNDU : MPFR_RNDD);
123           mpfr_abs (err_t, err_t, MPFR_RNDN); /* exact */
124           /* the absolute error on t is bounded by err_t * 2^(-w) */
125           mpfr_abs (err_u, t, MPFR_RNDU);
126           mpfr_mul_2ui (err_u, err_u, w, MPFR_RNDU); /* t * 2^w */
127           mpfr_add (err_u, err_u, err_t, MPFR_RNDU); /* max|t| * 2^w */
128           if (stop >= 2)
129             {
130               /* take into account the neglected terms: t * 2^w */
131               mpfr_div_2ui (err_s, err_s, w, MPFR_RNDU);
132               if (MPFR_IS_POS(t))
133                 mpfr_add (err_s, err_s, t, MPFR_RNDU);
134               else
135                 mpfr_sub (err_s, err_s, t, MPFR_RNDU);
136               mpfr_mul_2ui (err_s, err_s, w, MPFR_RNDU);
137               stop ++;
138             }
139           /* if k is odd, add to Q, otherwise to P */
140           else if (k & 1)
141             {
142               /* if k = 1 mod 4, add, otherwise subtract */
143               if ((k & 2) == 0)
144                 mpfr_add (Q, Q, t, MPFR_RNDN);
145               else
146                 mpfr_sub (Q, Q, t, MPFR_RNDN);
147               /* check if the next term is smaller than ulp(Q): if EXP(err_u)
148                  <= EXP(Q), since the current term is bounded by
149                  err_u * 2^(-w), it is bounded by ulp(Q) */
150               if (MPFR_GET_EXP (err_u) <= MPFR_GET_EXP (Q))
151                 stop ++;
152               else
153                 stop = 0;
154             }
155           else
156             {
157               /* if k = 0 mod 4, add, otherwise subtract */
158               if ((k & 2) == 0)
159                 mpfr_add (P, P, t, MPFR_RNDN);
160               else
161                 mpfr_sub (P, P, t, MPFR_RNDN);
162               /* check if the next term is smaller than ulp(P) */
163               if (MPFR_GET_EXP (err_u) <= MPFR_GET_EXP (P))
164                 stop ++;
165               else
166                 stop = 0;
167             }
168           mpfr_add (err_s, err_s, err_t, MPFR_RNDU);
169           /* the sum of the rounding errors on P and Q is bounded by
170              err_s * 2^(-w) */
171 
172           /* stop when start to diverge */
173           if (stop < 2 &&
174               ((MPFR_IS_POS(z) && mpfr_cmp_ui (z, (k + 1) / 2) < 0) ||
175                (MPFR_IS_NEG(z) && mpfr_cmp_si (z, - ((k + 1) / 2)) > 0)))
176             {
177               /* if we have to stop the series because it diverges, then
178                  increasing the precision will most probably fail, since
179                  we will stop to the same point, and thus compute a very
180                  similar approximation */
181               diverge = 1;
182               stop = 2; /* force stop */
183             }
184         }
185       /* the sum of the total errors on P and Q is bounded by err_s * 2^(-w) */
186 
187       /* Now combine: the sum of the rounding errors on P and Q is bounded by
188          err_s * 2^(-w), and the absolute error on s/c is bounded by 2^(1-w) */
189       if ((n & 1) == 0) /* n even: P * (sin + cos) + Q (cos - sin) for jn
190                                    Q * (sin + cos) + P (sin - cos) for yn */
191         {
192 #ifdef MPFR_JN
193           mpfr_mul (c, c, Q, MPFR_RNDN); /* Q * (sin - cos) */
194           mpfr_mul (s, s, P, MPFR_RNDN); /* P * (sin + cos) */
195 #else
196           mpfr_mul (c, c, P, MPFR_RNDN); /* P * (sin - cos) */
197           mpfr_mul (s, s, Q, MPFR_RNDN); /* Q * (sin + cos) */
198 #endif
199           err = MPFR_GET_EXP (c);
200           if (MPFR_GET_EXP (s) > err)
201             err = MPFR_EXP (s);
202 #ifdef MPFR_JN
203           mpfr_sub (s, s, c, MPFR_RNDN);
204 #else
205           mpfr_add (s, s, c, MPFR_RNDN);
206 #endif
207         }
208       else /* n odd: P * (sin - cos) + Q (cos + sin) for jn,
209                      Q * (sin - cos) - P (cos + sin) for yn */
210         {
211 #ifdef MPFR_JN
212           mpfr_mul (c, c, P, MPFR_RNDN); /* P * (sin - cos) */
213           mpfr_mul (s, s, Q, MPFR_RNDN); /* Q * (sin + cos) */
214 #else
215           mpfr_mul (c, c, Q, MPFR_RNDN); /* Q * (sin - cos) */
216           mpfr_mul (s, s, P, MPFR_RNDN); /* P * (sin + cos) */
217 #endif
218           err = MPFR_GET_EXP (c);
219           if (MPFR_GET_EXP (s) > err)
220             err = MPFR_EXP (s);
221 #ifdef MPFR_JN
222           mpfr_add (s, s, c, MPFR_RNDN);
223 #else
224           mpfr_sub (s, c, s, MPFR_RNDN);
225 #endif
226         }
227       if (MPFR_IS_ZERO(s))
228         goto clear; /* with ok=0 */
229       ok = 1;
230       if ((n & 2) != 0)
231         mpfr_neg (s, s, MPFR_RNDN);
232       if (MPFR_GET_EXP (s) > err)
233         err = MPFR_EXP (s);
234       /* the absolute error on s is bounded by P*err(s/c) + Q*err(s/c)
235          + err(P)*(s/c) + err(Q)*(s/c) + 3 * 2^(err - w - 1)
236          <= (|P|+|Q|) * 2^(1-w) + err_s * 2^(1-w) + 2^err * 2^(1-w),
237          since |c|, |old_s| <= 2. */
238       err2 = (MPFR_GET_EXP (P) >= MPFR_GET_EXP (Q))
239         ? MPFR_EXP (P) + 2 : MPFR_EXP (Q) + 2;
240       /* (|P| + |Q|) * 2^(1 - w) <= 2^(err2 - w) */
241       err = MPFR_GET_EXP (err_s) >= err ? MPFR_EXP (err_s) + 2 : err + 2;
242       /* err_s * 2^(1-w) + 2^old_err * 2^(1-w) <= 2^err * 2^(-w) */
243       err2 = (err >= err2) ? err + 1 : err2 + 1;
244       /* now the absolute error on s is bounded by 2^(err2 - w) */
245 
246       /* multiply by sqrt(1/(Pi*z)) */
247       mpfr_const_pi (c, MPFR_RNDN);     /* Pi, err <= 1 */
248       mpfr_mul (c, c, z, MPFR_RNDN);    /* err <= 2 */
249       mpfr_si_div (c, MPFR_IS_POS(z) ? 1 : -1, c, MPFR_RNDN); /* err <= 3 */
250       mpfr_sqrt (c, c, MPFR_RNDN);      /* err<=5/2, thus the absolute error is
251                                           bounded by 3*u*|c| for |u| <= 0.25 */
252       mpfr_mul (err_t, c, s, MPFR_SIGN(c)==MPFR_SIGN(s) ? MPFR_RNDU : MPFR_RNDD);
253       mpfr_abs (err_t, err_t, MPFR_RNDU);
254       mpfr_mul_ui (err_t, err_t, 3, MPFR_RNDU);
255       /* 3*2^(-w)*|old_c|*|s| [see below] is bounded by err_t * 2^(-w) */
256       err2 += MPFR_GET_EXP (c);
257       /* |old_c| * 2^(err2 - w) [see below] is bounded by 2^(err2-w) */
258       mpfr_mul (c, c, s, MPFR_RNDN);    /* the absolute error on c is bounded by
259                                           1/2 ulp(c) + 3*2^(-w)*|old_c|*|s|
260                                           + |old_c| * 2^(err2 - w) */
261       /* compute err_t * 2^(-w) + 1/2 ulp(c) = (err_t + 2^EXP(c)) * 2^(-w) */
262       err = (MPFR_GET_EXP (err_t) > MPFR_GET_EXP (c)) ?
263         MPFR_EXP (err_t) + 1 : MPFR_EXP (c) + 1;
264       /* err_t * 2^(-w) + 1/2 ulp(c) <= 2^(err - w) */
265       /* now err_t * 2^(-w) bounds 1/2 ulp(c) + 3*2^(-w)*|old_c|*|s| */
266       err = (err >= err2) ? err + 1 : err2 + 1;
267       /* the absolute error on c is bounded by 2^(err - w) */
268 
269       err -= MPFR_GET_EXP (c);
270 
271     clear:
272       mpfr_clear (s);
273       mpfr_clear (P);
274       mpfr_clear (Q);
275       mpfr_clear (t);
276       mpfr_clear (iz);
277       mpfr_clear (err_t);
278       mpfr_clear (err_s);
279       mpfr_clear (err_u);
280 
281       if (ok && MPFR_LIKELY (MPFR_CAN_ROUND (c, w - err, MPFR_PREC(res), r)))
282         break;
283       if (diverge != 0)
284         {
285           MPFR_ZIV_FREE (loop);
286           mpfr_clear (c);
287           return 0; /* means that the asymptotic expansion failed */
288         }
289       MPFR_ZIV_NEXT (loop, w);
290     }
291   MPFR_ZIV_FREE (loop);
292 
293   inex = mpfr_set4 (res, c, r, MPFR_IS_POS (z) || (n & 1) == 0 ?
294                     MPFR_SIGN (c) : - MPFR_SIGN (c));
295   mpfr_clear (c);
296 
297   /* for RNDF, mpfr_set or mpfr_neg may return 0, but if we return 0, it
298      would mean the asymptotic expansion failed, thus we return 1 instead */
299   return (r != MPFR_RNDF) ? inex : 1;
300 }
301