diff options
Diffstat (limited to 'REORG.TODO/sysdeps/ieee754/flt-32/e_gammaf_r.c')
-rw-r--r-- | REORG.TODO/sysdeps/ieee754/flt-32/e_gammaf_r.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/REORG.TODO/sysdeps/ieee754/flt-32/e_gammaf_r.c b/REORG.TODO/sysdeps/ieee754/flt-32/e_gammaf_r.c new file mode 100644 index 0000000000..1157dec2fe --- /dev/null +++ b/REORG.TODO/sysdeps/ieee754/flt-32/e_gammaf_r.c @@ -0,0 +1,212 @@ +/* Implementation of gamma function according to ISO C. + Copyright (C) 1997-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. + + 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 <math.h> +#include <math_private.h> +#include <float.h> + +/* Coefficients B_2k / 2k(2k-1) of x^-(2k-1) inside exp in Stirling's + approximation to gamma function. */ + +static const float gamma_coeff[] = + { + 0x1.555556p-4f, + -0xb.60b61p-12f, + 0x3.403404p-12f, + }; + +#define NCOEFF (sizeof (gamma_coeff) / sizeof (gamma_coeff[0])) + +/* Return gamma (X), for positive X less than 42, 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 float +gammaf_positive (float x, int *exp2_adj) +{ + int local_signgam; + if (x < 0.5f) + { + *exp2_adj = 0; + return __ieee754_expf (__ieee754_lgammaf_r (x + 1, &local_signgam)) / x; + } + else if (x <= 1.5f) + { + *exp2_adj = 0; + return __ieee754_expf (__ieee754_lgammaf_r (x, &local_signgam)); + } + else if (x < 2.5f) + { + *exp2_adj = 0; + float x_adj = x - 1; + return (__ieee754_expf (__ieee754_lgammaf_r (x_adj, &local_signgam)) + * x_adj); + } + else + { + float eps = 0; + float x_eps = 0; + float x_adj = x; + float prod = 1; + if (x < 4.0f) + { + /* Adjust into the range for applying Stirling's + approximation. */ + float n = __ceilf (4.0f - x); + x_adj = math_narrow_eval (x + n); + x_eps = (x - (x_adj - n)); + prod = __gamma_productf (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. */ + float exp_adj = -eps; + float x_adj_int = __roundf (x_adj); + float x_adj_frac = x_adj - x_adj_int; + int x_adj_log2; + float x_adj_mant = __frexpf (x_adj, &x_adj_log2); + if (x_adj_mant < (float) M_SQRT1_2) + { + x_adj_log2--; + x_adj_mant *= 2.0f; + } + *exp2_adj = x_adj_log2 * (int) x_adj_int; + float ret = (__ieee754_powf (x_adj_mant, x_adj) + * __ieee754_exp2f (x_adj_log2 * x_adj_frac) + * __ieee754_expf (-x_adj) + * __ieee754_sqrtf (2 * (float) M_PI / x_adj) + / prod); + exp_adj += x_eps * __ieee754_logf (x_adj); + float bsum = gamma_coeff[NCOEFF - 1]; + float 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 * __expm1f (exp_adj); + } +} + +float +__ieee754_gammaf_r (float x, int *signgamp) +{ + int32_t hx; + float ret; + + GET_FLOAT_WORD (hx, x); + + if (__glibc_unlikely ((hx & 0x7fffffff) == 0)) + { + /* Return value for x == 0 is Inf with divide by zero exception. */ + *signgamp = 0; + return 1.0 / x; + } + if (__builtin_expect (hx < 0, 0) + && (u_int32_t) hx < 0xff800000 && __rintf (x) == x) + { + /* Return value for integer x < 0 is NaN with invalid exception. */ + *signgamp = 0; + return (x - x) / (x - x); + } + if (__glibc_unlikely (hx == 0xff800000)) + { + /* x == -Inf. According to ISO this is NaN. */ + *signgamp = 0; + return x - x; + } + if (__glibc_unlikely ((hx & 0x7f800000) == 0x7f800000)) + { + /* Positive infinity (return positive infinity) or NaN (return + NaN). */ + *signgamp = 0; + return x + x; + } + + if (x >= 36.0f) + { + /* Overflow. */ + *signgamp = 0; + ret = math_narrow_eval (FLT_MAX * FLT_MAX); + return ret; + } + else + { + SET_RESTORE_ROUNDF (FE_TONEAREST); + if (x > 0.0f) + { + *signgamp = 0; + int exp2_adj; + float tret = gammaf_positive (x, &exp2_adj); + ret = __scalbnf (tret, exp2_adj); + } + else if (x >= -FLT_EPSILON / 4.0f) + { + *signgamp = 0; + ret = 1.0f / x; + } + else + { + float tx = __truncf (x); + *signgamp = (tx == 2.0f * __truncf (tx / 2.0f)) ? -1 : 1; + if (x <= -42.0f) + /* Underflow. */ + ret = FLT_MIN * FLT_MIN; + else + { + float frac = tx - x; + if (frac > 0.5f) + frac = 1.0f - frac; + float sinpix = (frac <= 0.25f + ? __sinf ((float) M_PI * frac) + : __cosf ((float) M_PI * (0.5f - frac))); + int exp2_adj; + float tret = (float) M_PI / (-x * sinpix + * gammaf_positive (-x, &exp2_adj)); + ret = __scalbnf (tret, -exp2_adj); + math_check_force_underflow_nonneg (ret); + } + } + ret = math_narrow_eval (ret); + } + if (isinf (ret) && x != 0) + { + if (*signgamp < 0) + { + ret = math_narrow_eval (-__copysignf (FLT_MAX, ret) * FLT_MAX); + ret = -ret; + } + else + ret = math_narrow_eval (__copysignf (FLT_MAX, ret) * FLT_MAX); + return ret; + } + else if (ret == 0) + { + if (*signgamp < 0) + { + ret = math_narrow_eval (-__copysignf (FLT_MIN, ret) * FLT_MIN); + ret = -ret; + } + else + ret = math_narrow_eval (__copysignf (FLT_MIN, ret) * FLT_MIN); + return ret; + } + else + return ret; +} +strong_alias (__ieee754_gammaf_r, __gammaf_r_finite) |