| /* Implementation of gamma function according to ISO C. | |
| Copyright (C) 1997-2018 Free Software Foundation, Inc. | |
| This file is part of the GNU C Library. | |
| Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997 and | |
| Jakub Jelinek <jj@ultra.linux.cz, 1999. | |
| The GNU C Library is free software; you can redistribute it and/or | |
| modify it under the terms of the GNU Lesser General Public | |
| License as published by the Free Software Foundation; either | |
| version 2.1 of the License, or (at your option) any later version. | |
| The GNU C Library is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public | |
| License along with the GNU C Library; if not, see | |
| <http://www.gnu.org/licenses/>. */ | |
| #include "quadmath-imp.h" | |
| __float128 | |
| tgammaq (__float128 x) | |
| { | |
| int sign; | |
| __float128 ret; | |
| ret = __quadmath_gammaq_r (x, &sign); | |
| return sign < 0 ? -ret : ret; | |
| } | |
| /* Coefficients B_2k / 2k(2k-1) of x^-(2k-1) inside exp in Stirling's | |
| approximation to gamma function. */ | |
| static const __float128 gamma_coeff[] = | |
| { | |
| 0x1.5555555555555555555555555555p-4Q, | |
| -0xb.60b60b60b60b60b60b60b60b60b8p-12Q, | |
| 0x3.4034034034034034034034034034p-12Q, | |
| -0x2.7027027027027027027027027028p-12Q, | |
| 0x3.72a3c5631fe46ae1d4e700dca8f2p-12Q, | |
| -0x7.daac36664f1f207daac36664f1f4p-12Q, | |
| 0x1.a41a41a41a41a41a41a41a41a41ap-8Q, | |
| -0x7.90a1b2c3d4e5f708192a3b4c5d7p-8Q, | |
| 0x2.dfd2c703c0cfff430edfd2c703cp-4Q, | |
| -0x1.6476701181f39edbdb9ce625987dp+0Q, | |
| 0xd.672219167002d3a7a9c886459cp+0Q, | |
| -0x9.cd9292e6660d55b3f712eb9e07c8p+4Q, | |
| 0x8.911a740da740da740da740da741p+8Q, | |
| -0x8.d0cc570e255bf59ff6eec24b49p+12Q, | |
| }; | |
| #define NCOEFF (sizeof (gamma_coeff) / sizeof (gamma_coeff[0])) | |
| /* Return gamma (X), for positive X less than 1775, in the form R * | |
| 2^(*EXP2_ADJ), where R is the return value and *EXP2_ADJ is set to | |
| avoid overflow or underflow in intermediate calculations. */ | |
| static __float128 | |
| gammal_positive (__float128 x, int *exp2_adj) | |
| { | |
| int local_signgam; | |
| if (x < 0.5Q) | |
| { | |
| *exp2_adj = 0; | |
| return expq (__quadmath_lgammaq_r (x + 1, &local_signgam)) / x; | |
| } | |
| else if (x <= 1.5Q) | |
| { | |
| *exp2_adj = 0; | |
| return expq (__quadmath_lgammaq_r (x, &local_signgam)); | |
| } | |
| else if (x < 12.5Q) | |
| { | |
| /* Adjust into the range for using exp (lgamma). */ | |
| *exp2_adj = 0; | |
| __float128 n = ceilq (x - 1.5Q); | |
| __float128 x_adj = x - n; | |
| __float128 eps; | |
| __float128 prod = __quadmath_gamma_productq (x_adj, 0, n, &eps); | |
| return (expq (__quadmath_lgammaq_r (x_adj, &local_signgam)) | |
| * prod * (1 + eps)); | |
| } | |
| else | |
| { | |
| __float128 eps = 0; | |
| __float128 x_eps = 0; | |
| __float128 x_adj = x; | |
| __float128 prod = 1; | |
| if (x < 24) | |
| { | |
| /* Adjust into the range for applying Stirling's | |
| approximation. */ | |
| __float128 n = ceilq (24 - x); | |
| x_adj = x + n; | |
| x_eps = (x - (x_adj - n)); | |
| prod = __quadmath_gamma_productq (x_adj - n, x_eps, n, &eps); | |
| } | |
| /* The result is now gamma (X_ADJ + X_EPS) / (PROD * (1 + EPS)). | |
| Compute gamma (X_ADJ + X_EPS) using Stirling's approximation, | |
| starting by computing pow (X_ADJ, X_ADJ) with a power of 2 | |
| factored out. */ | |
| __float128 exp_adj = -eps; | |
| __float128 x_adj_int = roundq (x_adj); | |
| __float128 x_adj_frac = x_adj - x_adj_int; | |
| int x_adj_log2; | |
| __float128 x_adj_mant = frexpq (x_adj, &x_adj_log2); | |
| if (x_adj_mant < M_SQRT1_2q) | |
| { | |
| x_adj_log2--; | |
| x_adj_mant *= 2; | |
| } | |
| *exp2_adj = x_adj_log2 * (int) x_adj_int; | |
| __float128 ret = (powq (x_adj_mant, x_adj) | |
| * exp2q (x_adj_log2 * x_adj_frac) | |
| * expq (-x_adj) | |
| * sqrtq (2 * M_PIq / x_adj) | |
| / prod); | |
| exp_adj += x_eps * logq (x_adj); | |
| __float128 bsum = gamma_coeff[NCOEFF - 1]; | |
| __float128 x_adj2 = x_adj * x_adj; | |
| for (size_t i = 1; i <= NCOEFF - 1; i++) | |
| bsum = bsum / x_adj2 + gamma_coeff[NCOEFF - 1 - i]; | |
| exp_adj += bsum / x_adj; | |
| return ret + ret * expm1q (exp_adj); | |
| } | |
| } | |
| __float128 | |
| __quadmath_gammaq_r (__float128 x, int *signgamp) | |
| { | |
| int64_t hx; | |
| uint64_t lx; | |
| __float128 ret; | |
| GET_FLT128_WORDS64 (hx, lx, x); | |
| if (((hx & 0x7fffffffffffffffLL) | lx) == 0) | |
| { | |
| /* Return value for x == 0 is Inf with divide by zero exception. */ | |
| *signgamp = 0; | |
| return 1.0 / x; | |
| } | |
| if (hx < 0 && (uint64_t) hx < 0xffff000000000000ULL && rintq (x) == x) | |
| { | |
| /* Return value for integer x < 0 is NaN with invalid exception. */ | |
| *signgamp = 0; | |
| return (x - x) / (x - x); | |
| } | |
| if (hx == 0xffff000000000000ULL && lx == 0) | |
| { | |
| /* x == -Inf. According to ISO this is NaN. */ | |
| *signgamp = 0; | |
| return x - x; | |
| } | |
| if ((hx & 0x7fff000000000000ULL) == 0x7fff000000000000ULL) | |
| { | |
| /* Positive infinity (return positive infinity) or NaN (return | |
| NaN). */ | |
| *signgamp = 0; | |
| return x + x; | |
| } | |
| if (x >= 1756) | |
| { | |
| /* Overflow. */ | |
| *signgamp = 0; | |
| return FLT128_MAX * FLT128_MAX; | |
| } | |
| else | |
| { | |
| SET_RESTORE_ROUNDF128 (FE_TONEAREST); | |
| if (x > 0) | |
| { | |
| *signgamp = 0; | |
| int exp2_adj; | |
| ret = gammal_positive (x, &exp2_adj); | |
| ret = scalbnq (ret, exp2_adj); | |
| } | |
| else if (x >= -FLT128_EPSILON / 4) | |
| { | |
| *signgamp = 0; | |
| ret = 1 / x; | |
| } | |
| else | |
| { | |
| __float128 tx = truncq (x); | |
| *signgamp = (tx == 2 * truncq (tx / 2)) ? -1 : 1; | |
| if (x <= -1775) | |
| /* Underflow. */ | |
| ret = FLT128_MIN * FLT128_MIN; | |
| else | |
| { | |
| __float128 frac = tx - x; | |
| if (frac > 0.5Q) | |
| frac = 1 - frac; | |
| __float128 sinpix = (frac <= 0.25Q | |
| ? sinq (M_PIq * frac) | |
| : cosq (M_PIq * (0.5Q - frac))); | |
| int exp2_adj; | |
| ret = M_PIq / (-x * sinpix | |
| * gammal_positive (-x, &exp2_adj)); | |
| ret = scalbnq (ret, -exp2_adj); | |
| math_check_force_underflow_nonneg (ret); | |
| } | |
| } | |
| } | |
| if (isinfq (ret) && x != 0) | |
| { | |
| if (*signgamp < 0) | |
| return -(-copysignq (FLT128_MAX, ret) * FLT128_MAX); | |
| else | |
| return copysignq (FLT128_MAX, ret) * FLT128_MAX; | |
| } | |
| else if (ret == 0) | |
| { | |
| if (*signgamp < 0) | |
| return -(-copysignq (FLT128_MIN, ret) * FLT128_MIN); | |
| else | |
| return copysignq (FLT128_MIN, ret) * FLT128_MIN; | |
| } | |
| else | |
| return ret; | |
| } |