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