1 /* Implementation of gamma function according to ISO C.
2 Copyright (C) 1997-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
20 #include <math_private.h>
21 #include <fenv_private.h>
22 #include <math-underflow.h>
24 #include <libm-alias-finite.h>
26 /* Coefficients B_2k / 2k(2k-1) of x^-(2k-1) inside exp in Stirling's
27 approximation to gamma function. */
29 static const long double gamma_coeff
[] =
31 0x1.5555555555555556p
-4L,
32 -0xb.60b60b60b60b60bp
-12L,
33 0x3.4034034034034034p
-12L,
34 -0x2.7027027027027028p
-12L,
35 0x3.72a3c5631fe46aep
-12L,
36 -0x7.daac36664f1f208p
-12L,
37 0x1.a41a41a41a41a41ap
-8L,
38 -0x7.90a1b2c3d4e5f708p
-8L,
41 #define NCOEFF (sizeof (gamma_coeff) / sizeof (gamma_coeff[0]))
43 /* Return gamma (X), for positive X less than 1766, in the form R *
44 2^(*EXP2_ADJ), where R is the return value and *EXP2_ADJ is set to
45 avoid overflow or underflow in intermediate calculations. */
48 gammal_positive (long double x
, int *exp2_adj
)
54 return __ieee754_expl (__ieee754_lgammal_r (x
+ 1, &local_signgam
)) / x
;
59 return __ieee754_expl (__ieee754_lgammal_r (x
, &local_signgam
));
63 /* Adjust into the range for using exp (lgamma). */
65 long double n
= ceill (x
- 1.5L);
66 long double x_adj
= x
- n
;
68 long double prod
= __gamma_productl (x_adj
, 0, n
, &eps
);
69 return (__ieee754_expl (__ieee754_lgammal_r (x_adj
, &local_signgam
))
70 * prod
* (1.0L + eps
));
75 long double x_eps
= 0;
76 long double x_adj
= x
;
80 /* Adjust into the range for applying Stirling's
82 long double n
= ceill (13.0L - x
);
84 x_eps
= (x
- (x_adj
- n
));
85 prod
= __gamma_productl (x_adj
- n
, x_eps
, n
, &eps
);
87 /* The result is now gamma (X_ADJ + X_EPS) / (PROD * (1 + EPS)).
88 Compute gamma (X_ADJ + X_EPS) using Stirling's approximation,
89 starting by computing pow (X_ADJ, X_ADJ) with a power of 2
91 long double exp_adj
= -eps
;
92 long double x_adj_int
= roundl (x_adj
);
93 long double x_adj_frac
= x_adj
- x_adj_int
;
95 long double x_adj_mant
= __frexpl (x_adj
, &x_adj_log2
);
96 if (x_adj_mant
< M_SQRT1_2l
)
101 *exp2_adj
= x_adj_log2
* (int) x_adj_int
;
102 long double ret
= (__ieee754_powl (x_adj_mant
, x_adj
)
103 * __ieee754_exp2l (x_adj_log2
* x_adj_frac
)
104 * __ieee754_expl (-x_adj
)
105 * sqrtl (2 * M_PIl
/ x_adj
)
107 exp_adj
+= x_eps
* __ieee754_logl (x_adj
);
108 long double bsum
= gamma_coeff
[NCOEFF
- 1];
109 long double x_adj2
= x_adj
* x_adj
;
110 for (size_t i
= 1; i
<= NCOEFF
- 1; i
++)
111 bsum
= bsum
/ x_adj2
+ gamma_coeff
[NCOEFF
- 1 - i
];
112 exp_adj
+= bsum
/ x_adj
;
113 return ret
+ ret
* __expm1l (exp_adj
);
118 __ieee754_gammal_r (long double x
, int *signgamp
)
123 GET_LDOUBLE_WORDS (es
, hx
, lx
, x
);
125 if (__glibc_unlikely (((es
& 0x7fff) | hx
| lx
) == 0))
127 /* Return value for x == 0 is Inf with divide by zero exception. */
131 if (__glibc_unlikely (es
== 0xffffffff && ((hx
& 0x7fffffff) | lx
) == 0))
133 /* x == -Inf. According to ISO this is NaN. */
137 if (__glibc_unlikely ((es
& 0x7fff) == 0x7fff))
139 /* Positive infinity (return positive infinity) or NaN (return
144 if (__builtin_expect ((es
& 0x8000) != 0, 0) && rintl (x
) == x
)
146 /* Return value for integer x < 0 is NaN with invalid exception. */
148 return (x
- x
) / (x
- x
);
155 return LDBL_MAX
* LDBL_MAX
;
159 SET_RESTORE_ROUNDL (FE_TONEAREST
);
164 ret
= gammal_positive (x
, &exp2_adj
);
165 ret
= __scalbnl (ret
, exp2_adj
);
167 else if (x
>= -LDBL_EPSILON
/ 4.0L)
174 long double tx
= truncl (x
);
175 *signgamp
= (tx
== 2.0L * truncl (tx
/ 2.0L)) ? -1 : 1;
178 ret
= LDBL_MIN
* LDBL_MIN
;
181 long double frac
= tx
- x
;
184 long double sinpix
= (frac
<= 0.25L
185 ? __sinl (M_PIl
* frac
)
186 : __cosl (M_PIl
* (0.5L - frac
)));
188 ret
= M_PIl
/ (-x
* sinpix
189 * gammal_positive (-x
, &exp2_adj
));
190 ret
= __scalbnl (ret
, -exp2_adj
);
191 math_check_force_underflow_nonneg (ret
);
195 if (isinf (ret
) && x
!= 0)
198 return -(-copysignl (LDBL_MAX
, ret
) * LDBL_MAX
);
200 return copysignl (LDBL_MAX
, ret
) * LDBL_MAX
;
205 return -(-copysignl (LDBL_MIN
, ret
) * LDBL_MIN
);
207 return copysignl (LDBL_MIN
, ret
) * LDBL_MIN
;
212 libm_alias_finite (__ieee754_gammal_r
, __gammal_r
)