xref: /netbsd-src/external/lgpl3/mpfr/dist/src/beta.c (revision ba125506a622fe649968631a56eba5d42ff57863)
1 /* mpfr_beta -- beta function
2 
3 Copyright 2017-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 #define MPFR_NEED_LONGLONG_H /* for MPFR_INT_CEIL_LOG2 */
24 #include "mpfr-impl.h"
25 
26 /* use formula (6.2.2) from Abramowitz & Stegun:
27    beta(z,w) = gamma(z)*gamma(w)/gamma(z+w) */
28 int
mpfr_beta(mpfr_ptr r,mpfr_srcptr z,mpfr_srcptr w,mpfr_rnd_t rnd_mode)29 mpfr_beta (mpfr_ptr r, mpfr_srcptr z, mpfr_srcptr w, mpfr_rnd_t rnd_mode)
30 {
31   mpfr_exp_t emin, emax;
32   mpfr_uexp_t pmin;
33   mpfr_prec_t prec;
34   mpfr_t z_plus_w, tmp, tmp2;
35   int inex, w_integer;
36   MPFR_GROUP_DECL (group);
37   MPFR_ZIV_DECL (loop);
38   MPFR_SAVE_EXPO_DECL (expo);
39 
40   if (mpfr_less_p (z, w))
41     return mpfr_beta (r, w, z, rnd_mode);
42 
43   /* Now, either z and w are unordered (at least one is a NaN), or z >= w. */
44 
45   if (MPFR_ARE_SINGULAR (z, w))
46     {
47       /* if z or w is NaN, return NaN */
48       if (MPFR_IS_NAN (z) || MPFR_IS_NAN (w))
49         {
50           MPFR_SET_NAN (r);
51           MPFR_RET_NAN;
52         }
53       else if (MPFR_IS_INF (z) || MPFR_IS_INF (w))
54         {
55           /* Since we have z >= w:
56              if z = +Inf and w > 0, then r = +0 (including w = +Inf);
57              if z = +Inf and w = 0, then r = NaN
58                [beta(z,1/log(z)) tends to +Inf whereas
59                 beta(z,1/log(log(z))) tends to +0]
60              if z = +Inf and w < 0:
61                 if w is an integer or -Inf: r = NaN
62                 if -2k-1 < w < -2k:   r = -Inf
63                 if -2k-2 < w < -2k-1: r = +Inf
64              if w = -Inf and z is finite and not an integer:
65                 beta(z,t) for t going to -Inf oscillates between positive and
66                 negative values, with poles around integer values of t, thus
67                 beta(z,w) gives NaN;
68              if w = -Inf and z is an integer:
69                 beta(z,w) gives +0 for z even > 0, -0 for z odd > 0,
70                 NaN for z <= 0;
71              if z = -Inf (then w = -Inf too): r = NaN */
72           if (MPFR_IS_INF (z) && MPFR_IS_POS(z)) /* z = +Inf */
73             {
74               if (mpfr_cmp_ui (w, 0) > 0)
75                 {
76                   MPFR_SET_ZERO(r);
77                   MPFR_SET_POS(r);
78                   MPFR_RET(0);
79                 }
80               else if (MPFR_IS_ZERO(w) || MPFR_IS_INF(w) || mpfr_integer_p (w))
81                 {
82                   MPFR_SET_NAN(r);
83                   MPFR_RET_NAN;
84                 }
85               else
86                 {
87                   long q;
88                   mpfr_t t;
89 
90                   MPFR_SAVE_EXPO_MARK (expo);
91                   mpfr_init2 (t, MPFR_PREC_MIN);
92                   mpfr_set_ui (t, 1, MPFR_RNDN);
93                   mpfr_fmodquo (t, &q, w, t, MPFR_RNDD);
94                   mpfr_clear (t);
95                   MPFR_SAVE_EXPO_FREE (expo);
96                   /* q contains the low bits of trunc(w) where trunc() rounds
97                      toward zero, thus if q is odd, then -2k-2 < w < -2k-1 */
98                   MPFR_SET_INF(r);
99                   if ((unsigned long) q & 1)
100                     MPFR_SET_NEG(r);
101                   else
102                     MPFR_SET_POS(r);
103                   MPFR_RET(0);
104                 }
105             }
106           else if (MPFR_IS_INF(w)) /* w = -Inf */
107             {
108               if (mpfr_cmp_ui (z, 0) <= 0 || !mpfr_integer_p (z))
109                 {
110                   MPFR_SET_NAN(r);
111                   MPFR_RET_NAN;
112                 }
113               else
114                 {
115                   MPFR_SET_ZERO(r);
116                   if (mpfr_odd_p (z))
117                     MPFR_SET_NEG(r);
118                   else
119                     MPFR_SET_POS(r);
120                   MPFR_RET(0);
121                 }
122             }
123         }
124       else /* z or w is 0 */
125         {
126           /* If x is not a non-positive integer, Gamma(x) is regular, so that
127              when y -> 0 with either y >= 0 or y <= 0,
128                Beta(x,y) ~ Gamma(x) * Gamma(y) / Gamma(x) = Gamma(y)
129              Gamma(y) tends to an infinity of the same sign as y.
130              Thus Beta(x,y) should be an infinity of the same sign as y.
131            */
132           if (mpfr_cmp_ui (z, 0) != 0) /* then w is +0 or -0 and z > 0 */
133             {
134               /* beta(z,+0) = +Inf, beta(z,-0) = -Inf (see above) */
135               MPFR_SET_INF(r);
136               MPFR_SET_SAME_SIGN(r,w);
137               MPFR_SET_DIVBY0 ();
138               MPFR_RET(0);
139             }
140           else if (mpfr_cmp_ui (w, 0) != 0) /* then z is +0 or -0 and w < 0 */
141             {
142               if (mpfr_integer_p (w))
143                 {
144                   /* For small u > 0, Beta(2u,w+u) and Beta(2u,w-u) have
145                      opposite signs, so that they tend to infinities of
146                      opposite signs when u -> 0. Thus the result is NaN. */
147                   MPFR_SET_NAN(r);
148                   MPFR_RET_NAN;
149                 }
150               else
151                 {
152                   /* beta(+0,w) = +Inf, beta(-0,w) = -Inf (see above) */
153                   MPFR_SET_INF(r);
154                   MPFR_SET_SAME_SIGN(r,z);
155                   MPFR_SET_DIVBY0 ();
156                   MPFR_RET(0);
157                 }
158             }
159           else /* w = z = 0:
160                   beta(+0,+0) = +Inf
161                   beta(-0,-0) = -Inf
162                   beta(+0,-0) = NaN */
163             {
164               if (MPFR_SIGN(z) == MPFR_SIGN(w))
165                 {
166                   MPFR_SET_INF(r);
167                   MPFR_SET_SAME_SIGN(r,z);
168                   MPFR_SET_DIVBY0 ();
169                   MPFR_RET(0);
170                 }
171               else
172                 {
173                   MPFR_SET_NAN(r);
174                   MPFR_RET_NAN;
175                 }
176             }
177         }
178     }
179 
180   /* special case when w is a negative integer */
181   w_integer = mpfr_integer_p (w);
182   if (w_integer && MPFR_IS_NEG(w))
183     {
184       /* if z < 0 or z+w > 0, or z is not an integer, return NaN */
185       if (MPFR_IS_NEG(z) || mpfr_cmpabs (z, w) > 0 || !mpfr_integer_p (z))
186         {
187           MPFR_SET_NAN(r);
188           MPFR_RET_NAN;
189         }
190       /* If z+w = 0, the result is 1/z. */
191       if (mpfr_cmpabs (z, w) == 0)
192         return mpfr_ui_div (r, 1, z, rnd_mode);
193       /* Now z is an integer and z+w <= 0: return (-1)^z*beta(z,1-w-z).
194          Since z and w are of opposite signs, |z+w| <= max(|z|,|w|). */
195       emax = MAX (MPFR_EXP(z), MPFR_EXP(w));
196       mpfr_init2 (z_plus_w, (mpfr_prec_t) emax);
197       inex = mpfr_add (z_plus_w, z, w, MPFR_RNDN);
198       MPFR_ASSERTN(inex == 0);
199       inex = mpfr_ui_sub (z_plus_w, 1, z_plus_w, MPFR_RNDN);
200       MPFR_ASSERTN(inex == 0);
201       if (mpfr_odd_p (z))
202         {
203           inex = -mpfr_beta (r, z, z_plus_w, MPFR_INVERT_RND (rnd_mode));
204           MPFR_CHANGE_SIGN(r);
205         }
206       else
207         inex = mpfr_beta (r, z, z_plus_w, rnd_mode);
208       mpfr_clear (z_plus_w);
209       return inex;
210     }
211 
212   /* special case when z is a negative integer: here w < z and w is not an
213      integer */
214   if (mpfr_integer_p (z) && MPFR_IS_NEG(z))
215     {
216       MPFR_SET_NAN(r);
217       MPFR_RET_NAN;
218     }
219 
220   MPFR_SAVE_EXPO_MARK (expo);
221 
222   /* compute the smallest precision such that z + w is exact */
223   emax = MAX (MPFR_EXP(z), MPFR_EXP(w));
224   emin = MIN (MPFR_EXP(z) - MPFR_PREC(z), MPFR_EXP(w) - MPFR_PREC(w));
225   MPFR_ASSERTD (emax >= emin);
226   /* Thus the math value of emax - emin is representable in mpfr_uexp_t. */
227   pmin = (mpfr_uexp_t) emax - emin;
228   /* If z and w have same sign, their sum can have exponent emax + 1. */
229   pmin += 1;
230   if (pmin > MPFR_PREC_MAX) /* FIXME: check if result can differ from NaN. */
231     {
232       MPFR_SAVE_EXPO_FREE (expo);
233       MPFR_SET_NAN(r);
234       MPFR_RET_NAN;
235     }
236   MPFR_ASSERTN (pmin <= MPFR_PREC_MAX);  /* detect integer overflow */
237   mpfr_init2 (z_plus_w, (mpfr_prec_t) pmin);
238   inex = mpfr_add (z_plus_w, z, w, MPFR_RNDN);
239   /* if z+w overflows with rounding to nearest, then w must be larger than
240      1/2*ulp(z), thus we have an underflow. */
241   if (MPFR_IS_INF(z_plus_w))
242     {
243       mpfr_clear (z_plus_w);
244       MPFR_SAVE_EXPO_FREE (expo);
245       return mpfr_underflow (r, rnd_mode, 1);
246     }
247   MPFR_ASSERTN(inex == 0);
248 
249   /* If z+w is 0 or a negative integer, return +0 when w (and thus z) is not
250      an integer. Indeed, gamma(z) and gamma(w) are regular numbers, and
251      gamma(z+w) is Inf, thus 1/gamma(z+w) is zero. Unless there is a rule
252      to choose the sign of 0, we choose +0. */
253   if (mpfr_cmp_ui (z_plus_w, 0) <= 0 && !w_integer
254       && mpfr_integer_p (z_plus_w))
255     {
256       mpfr_clear (z_plus_w);
257       MPFR_SAVE_EXPO_FREE (expo);
258       MPFR_SET_ZERO(r);
259       MPFR_SET_POS(r);
260       MPFR_RET(0);
261     }
262 
263   prec = MPFR_PREC(r);
264   prec += MPFR_INT_CEIL_LOG2 (prec);
265   MPFR_GROUP_INIT_2 (group, prec, tmp, tmp2);
266   MPFR_ZIV_INIT (loop, prec);
267   for (;;)
268     {
269       unsigned int inex2;  /* unsigned due to bitwise operations */
270 
271       MPFR_GROUP_REPREC_2 (group, prec, tmp, tmp2);
272       inex2 = mpfr_gamma (tmp, z, MPFR_RNDN);
273       /* tmp = gamma(z) * (1 + theta) with |theta| <= 2^-prec */
274       inex2 |= mpfr_gamma (tmp2, w, MPFR_RNDN);
275       /* tmp2 = gamma(w) * (1 + theta2) with |theta2| <= 2^-prec */
276       inex2 |= mpfr_mul (tmp, tmp, tmp2, MPFR_RNDN);
277       /* tmp = gamma(z)*gamma(w) * (1 + theta3)^3 with |theta3| <= 2^-prec */
278       inex2 |= mpfr_gamma (tmp2, z_plus_w, MPFR_RNDN);
279       /* tmp2 = gamma(z+w) * (1 + theta4) with |theta4| <= 2^-prec */
280       inex2 |= mpfr_div (tmp, tmp, tmp2, MPFR_RNDN);
281       /* tmp = gamma(z)*gamma(w)/gamma(z+w) * (1 + theta5)^5
282          with |theta5| <= 2^-prec. For prec >= 3, we have
283          |(1 + theta5)^5 - 1| <= 7 * 2^(-prec), thus the error is bounded
284          by 7 ulps */
285 
286       if (MPFR_IS_NAN(tmp)) /* FIXME: most probably gamma(z)*gamma(w) = +-Inf,
287                                and gamma(z+w) = +-Inf, can we do better? */
288         {
289           mpfr_clear (z_plus_w);
290           MPFR_ZIV_FREE (loop);
291           MPFR_GROUP_CLEAR (group);
292           MPFR_SAVE_EXPO_FREE (expo);
293           MPFR_SET_NAN(r);
294           MPFR_RET_NAN;
295         }
296 
297       MPFR_ASSERTN(mpfr_regular_p (tmp));
298 
299       /* if inex2 = 0, then tmp is exactly beta(z,w) */
300       if (inex2 == 0 ||
301           MPFR_LIKELY (MPFR_CAN_ROUND (tmp, prec - 3, MPFR_PREC(r), rnd_mode)))
302         break;
303 
304       /* beta(1,+/-2^(-k)) = +/-2^k is exact, and cannot be detected above
305          since gamma(+/-2^(-k)) is not exact */
306       if (mpfr_cmp_ui (z, 1) == 0)
307         {
308           mpfr_exp_t expw = mpfr_get_exp (w);
309           if (mpfr_cmp_ui_2exp (w, 1, expw - 1) == 0)
310             {
311               /* since z >= w, this will only match w <= 1 */
312               mpfr_set_ui_2exp (tmp, 1, 1 - expw, MPFR_RNDN);
313               break;
314             }
315           else if (mpfr_cmp_si_2exp (w, -1, expw - 1) == 0)
316             {
317               mpfr_set_si_2exp (tmp, -1, 1 - expw, MPFR_RNDN);
318               break;
319             }
320         }
321 
322       /* beta(2^k,1) = 1/2^k for k > 0 (k <= 0 was already tested above) */
323       if (mpfr_cmp_ui (w, 1) == 0 &&
324           mpfr_cmp_ui_2exp (z, 1, MPFR_EXP(z) - 1) == 0)
325         {
326           mpfr_set_ui_2exp (tmp, 1, 1 - MPFR_EXP(z), MPFR_RNDN);
327           break;
328         }
329 
330       /* beta(2,-0.5) = -4 */
331       if (mpfr_cmp_ui (z, 2) == 0 && mpfr_cmp_si_2exp (w, -1, -1) == 0)
332         {
333           mpfr_set_si_2exp (tmp, -1, 2, MPFR_RNDN);
334           break;
335         }
336 
337       MPFR_ZIV_NEXT (loop, prec);
338     }
339   MPFR_ZIV_FREE (loop);
340   inex = mpfr_set (r, tmp, rnd_mode);
341   MPFR_GROUP_CLEAR (group);
342   mpfr_clear (z_plus_w);
343   MPFR_SAVE_EXPO_FREE (expo);
344   return mpfr_check_range (r, inex, rnd_mode);
345 }
346