about summary refs log tree commit diff
path: root/src/math/tgamma.c
diff options
context:
space:
mode:
authorSzabolcs Nagy <nsz@port70.net>2013-11-21 01:01:57 +0000
committerSzabolcs Nagy <nsz@port70.net>2013-11-21 01:01:57 +0000
commitebbaf2180e6e32043837f570982c2ee86cf19eae (patch)
tree715c3f74bc82d25994b76298ea3919d9756eb76a /src/math/tgamma.c
parent326e5c2e27224e3323e54f37621d55c40ebae87c (diff)
downloadmusl-ebbaf2180e6e32043837f570982c2ee86cf19eae.tar.gz
musl-ebbaf2180e6e32043837f570982c2ee86cf19eae.tar.xz
musl-ebbaf2180e6e32043837f570982c2ee86cf19eae.zip
math: lgamma cleanup (simpler sin(pi*x) for the negative case)
* simplify sin_pi(x) (don't care about inexact here, the result is
  inexact anyway, and x is not so small to underflow)
* in lgammal add the previously removed special case for x==1 and
  x==2 (to fix the sign of zero in downward rounding mode)
* only define lgammal on supported long double platforms
* change tgamma so the generated code is a bit smaller
Diffstat (limited to 'src/math/tgamma.c')
-rw-r--r--src/math/tgamma.c40
1 files changed, 20 insertions, 20 deletions
diff --git a/src/math/tgamma.c b/src/math/tgamma.c
index f91af735..28f6e0f8 100644
--- a/src/math/tgamma.c
+++ b/src/math/tgamma.c
@@ -26,7 +26,7 @@ most ideas and constants are from boost and python
 
 static const double pi = 3.141592653589793238462643383279502884;
 
-/* sin(pi x) with x > 0 && isnormal(x) assumption */
+/* sin(pi x) with x > 0x1p-100, if sin(pi*x)==0 the sign is arbitrary */
 static double sinpi(double x)
 {
 	int n;
@@ -49,8 +49,7 @@ static double sinpi(double x)
 	case 1:
 		return __cos(x, 0);
 	case 2:
-		/* sin(0-x) and -sin(x) have different sign at 0 */
-		return __sin(0-x, 0, 0);
+		return __sin(-x, 0, 0);
 	case 3:
 		return -__cos(x, 0);
 	}
@@ -108,35 +107,33 @@ static double S(double x)
 
 double tgamma(double x)
 {
-	double absx, y, dy, z, r;
+	union {double f; uint64_t i;} u = {x};
+	double absx, y;
+	double_t dy, z, r;
+	uint32_t ix = u.i>>32 & 0x7fffffff;
+	int sign = u.i>>63;
 
 	/* special cases */
-	if (!isfinite(x))
+	if (ix >= 0x7ff00000)
 		/* tgamma(nan)=nan, tgamma(inf)=inf, tgamma(-inf)=nan with invalid */
 		return x + INFINITY;
+	if (ix < (0x3ff-54)<<20)
+		/* |x| < 2^-54: tgamma(x) ~ 1/x, +-0 raises div-by-zero */
+		return 1/x;
 
 	/* integer arguments */
 	/* raise inexact when non-integer */
 	if (x == floor(x)) {
-		if (x == 0)
-			/* tgamma(+-0)=+-inf with divide-by-zero */
-			return 1/x;
-		if (x < 0)
+		if (sign)
 			return 0/0.0;
 		if (x <= sizeof fact/sizeof *fact)
 			return fact[(int)x - 1];
 	}
 
-	absx = fabs(x);
-
-	/* x ~ 0: tgamma(x) ~ 1/x */
-	if (absx < 0x1p-54)
-		return 1/x;
-
 	/* x >= 172: tgamma(x)=inf with overflow */
 	/* x =< -184: tgamma(x)=+-0 with underflow */
-	if (absx >= 184) {
-		if (x < 0) {
+	if (ix >= 0x40670000) { /* |x| >= 184 */
+		if (sign) {
 			FORCE_EVAL((float)(0x1p-126/x));
 			if (floor(x) * 0.5 == floor(x * 0.5))
 				return 0;
@@ -146,6 +143,8 @@ double tgamma(double x)
 		return x;
 	}
 
+	absx = sign ? -x : x;
+
 	/* handle the error of x + g - 0.5 */
 	y = absx + gmhalf;
 	if (absx > gmhalf) {
@@ -160,20 +159,21 @@ double tgamma(double x)
 	r = S(absx) * exp(-y);
 	if (x < 0) {
 		/* reflection formula for negative x */
+		/* sinpi(absx) is not 0, integers are already handled */
 		r = -pi / (sinpi(absx) * absx * r);
 		dy = -dy;
 		z = -z;
 	}
 	r += dy * (gmhalf+0.5) * r / y;
 	z = pow(y, 0.5*z);
-	r = r * z * z;
-	return r;
+	y = r * z * z;
+	return y;
 }
 
 #if 0
 double __lgamma_r(double x, int *sign)
 {
-	double r, absx, z, zz, w;
+	double r, absx;
 
 	*sign = 1;