about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--include/tgmath.h70
-rw-r--r--src/math/acos.c4
-rw-r--r--src/math/acosl.c4
-rw-r--r--src/math/asin.c4
-rw-r--r--src/math/asinh.c2
-rw-r--r--src/math/asinhl.c2
-rw-r--r--src/math/asinl.c4
-rw-r--r--src/math/atan.c4
-rw-r--r--src/math/atan2l.c22
-rw-r--r--src/math/atanl.c4
-rw-r--r--src/math/cosh.c73
-rw-r--r--src/math/coshf.c49
-rw-r--r--src/math/coshl.c69
-rw-r--r--src/math/exp2f.c4
-rw-r--r--src/math/sinh.c90
-rw-r--r--src/math/sinhf.c66
-rw-r--r--src/math/sinhl.c87
-rw-r--r--src/math/tanh.c94
-rw-r--r--src/math/tanhf.c70
-rw-r--r--src/math/tanhl.c92
-rw-r--r--src/math/tgammal.c51
21 files changed, 298 insertions, 567 deletions
diff --git a/include/tgmath.h b/include/tgmath.h
index 832b052b..e41ccac9 100644
--- a/include/tgmath.h
+++ b/include/tgmath.h
@@ -13,7 +13,7 @@ sizeof(double) == sizeof(long double)
 #include <math.h>
 #include <complex.h>
 
-#define __IS_FP(x) !!((1?1:(x))/2)
+#define __IS_FP(x) (sizeof((x)+1ULL) == sizeof((x)+1.0f))
 #define __IS_CX(x) (__IS_FP(x) && sizeof(x) == sizeof((x)+I))
 #define __IS_REAL(x) (__IS_FP(x) && 2*sizeof(x) == sizeof((x)+I))
 
@@ -27,34 +27,52 @@ sizeof(double) == sizeof(long double)
 /* return type */
 
 #ifdef __GNUC__
+/*
+the result must be casted to the right type
+(otherwise the result type is determined by the conversion
+rules applied to all the function return types so it is long
+double or long double complex except for integral functions)
+
+this cannot be done in c99, so the typeof gcc extension is
+used and that the type of ?: depends on wether an operand is
+a null pointer constant or not
+(in c11 _Generic can be used)
+
+the c arguments below must be integer constant expressions
+so they can be in null pointer constants
+(__IS_FP above was carefully chosen this way)
+*/
+/* if c then t else void */
+#define __type1(c,t) __typeof__(*(0?(t*)0:(void*)!(c)))
+/* if c then t1 else t2 */
+#define __type2(c,t1,t2) __typeof__(*(0?(__type1(c,t1)*)0:(__type1(!(c),t2)*)0))
 /* cast to double when x is integral, otherwise use typeof(x) */
-#define __RETCAST(x) (__typeof__(*( \
-	0 ? (__typeof__(0 ? (double *)0 : (void *)__IS_FP(x)))0 : \
-	    (__typeof__(0 ? (__typeof__(x) *)0 : (void *)!__IS_FP(x)))0 )))
-/* 2 args case, consider complex types (for cpow) */
-#define __RETCAST_2(x, y) (__typeof__(*( \
-	0 ? (__typeof__(0 ? (double *)0 : \
-		(void *)!((!__IS_FP(x) || !__IS_FP(y)) && __FLT((x)+(y)+1.0f))))0 : \
-	0 ? (__typeof__(0 ? (double complex *)0 : \
-		(void *)!((!__IS_FP(x) || !__IS_FP(y)) && __FLTCX((x)+(y)))))0 : \
-	    (__typeof__(0 ? (__typeof__((x)+(y)) *)0 : \
-		(void *)((!__IS_FP(x) || !__IS_FP(y)) && (__FLT((x)+(y)+1.0f) || __FLTCX((x)+(y))))))0 )))
-/* 3 args case, don't consider complex types (fma only) */
-#define __RETCAST_3(x, y, z) (__typeof__(*( \
-	0 ? (__typeof__(0 ? (double *)0 : \
-		(void *)!((!__IS_FP(x) || !__IS_FP(y) || !__IS_FP(z)) && __FLT((x)+(y)+(z)+1.0f))))0 : \
-	    (__typeof__(0 ? (__typeof__((x)+(y)) *)0 : \
-		(void *)((!__IS_FP(x) || !__IS_FP(y) || !__IS_FP(z)) && __FLT((x)+(y)+(z)+1.0f))))0 )))
+#define __RETCAST(x) ( \
+	__type2(__IS_FP(x), __typeof__(x), double))
+/* 2 args case, should work for complex types (cpow) */
+#define __RETCAST_2(x, y) ( \
+	__type2(__IS_FP(x) && __IS_FP(y), \
+		__typeof__((x)+(y)), \
+		__typeof__((x)+(y)+1.0)))
+/* 3 args case (fma only) */
+#define __RETCAST_3(x, y, z) ( \
+	__type2(__IS_FP(x) && __IS_FP(y) && __IS_FP(z), \
+		__typeof__((x)+(y)+(z)), \
+		__typeof__((x)+(y)+(z)+1.0)))
 /* drop complex from the type of x */
-#define __TO_REAL(x) *( \
-	0 ? (__typeof__(0 ? (double *)0 : (void *)!__DBLCX(x)))0 : \
-	0 ? (__typeof__(0 ? (float *)0 : (void *)!__FLTCX(x)))0 : \
-	0 ? (__typeof__(0 ? (long double *)0 : (void *)!__LDBLCX(x)))0 : \
-	    (__typeof__(0 ? (__typeof__(x) *)0 : (void *)__IS_CX(x)))0 )
+/* TODO: wrong when sizeof(long double)==sizeof(double) */
+#define __RETCAST_REAL(x) (  \
+	__type2(__IS_FP(x) && sizeof((x)+I) == sizeof(float complex), float, \
+	__type2(sizeof((x)+1.0+I) == sizeof(double complex), double, \
+		long double)))
+/* add complex to the type of x */
+#define __RETCAST_CX(x) (__typeof__(__RETCAST(x)0+I))
 #else
 #define __RETCAST(x)
 #define __RETCAST_2(x, y)
 #define __RETCAST_3(x, y, z)
+#define __RETCAST_REAL(x)
+#define __RETCAST_CX(x)
 #endif
 
 /* function selection */
@@ -76,12 +94,12 @@ sizeof(double) == sizeof(long double)
 	__LDBL((x)+(y)) ? fun ## l (x, y) : \
 	fun(x, y) ))
 
-#define __tg_complex(fun, x) (__RETCAST((x)+I)( \
+#define __tg_complex(fun, x) (__RETCAST_CX(x)( \
 	__FLTCX((x)+I) && __IS_FP(x) ? fun ## f (x) : \
 	__LDBLCX((x)+I) ? fun ## l (x) : \
 	fun(x) ))
 
-#define __tg_complex_retreal(fun, x) (__RETCAST(__TO_REAL(x))( \
+#define __tg_complex_retreal(fun, x) (__RETCAST_REAL(x)( \
 	__FLTCX((x)+I) && __IS_FP(x) ? fun ## f (x) : \
 	__LDBLCX((x)+I) ? fun ## l (x) : \
 	fun(x) ))
@@ -115,7 +133,7 @@ sizeof(double) == sizeof(long double)
 	__LDBL((x)+(y)) ? powl(x, y) : \
 	pow(x, y) ))
 
-#define __tg_real_complex_fabs(x) (__RETCAST(__TO_REAL(x))( \
+#define __tg_real_complex_fabs(x) (__RETCAST_REAL(x)( \
 	__FLTCX(x) ? cabsf(x) : \
 	__DBLCX(x) ? cabs(x) : \
 	__LDBLCX(x) ? cabsl(x) : \
diff --git a/src/math/acos.c b/src/math/acos.c
index be95d25e..cd5d06a6 100644
--- a/src/math/acos.c
+++ b/src/math/acos.c
@@ -72,7 +72,7 @@ double acos(double x)
 		if ((ix-0x3ff00000 | lx) == 0) {
 			/* acos(1)=0, acos(-1)=pi */
 			if (hx >> 31)
-				return 2*pio2_hi + 0x1p-1000;
+				return 2*pio2_hi + 0x1p-120f;
 			return 0;
 		}
 		return 0/(x-x);
@@ -80,7 +80,7 @@ double acos(double x)
 	/* |x| < 0.5 */
 	if (ix < 0x3fe00000) {
 		if (ix <= 0x3c600000)  /* |x| < 2**-57 */
-			return pio2_hi + 0x1p-1000;
+			return pio2_hi + 0x1p-120f;
 		return pio2_hi - (x - (pio2_lo-x*R(x*x)));
 	}
 	/* x < -0.5 */
diff --git a/src/math/acosl.c b/src/math/acosl.c
index 9e9b01e4..9e7b7fb3 100644
--- a/src/math/acosl.c
+++ b/src/math/acosl.c
@@ -38,14 +38,14 @@ long double acosl(long double x)
 			((u.bits.manh & ~LDBL_NBIT) | u.bits.manl) == 0) {
 			if (expsign > 0)
 				return 0;  /* acos(1) = 0 */
-			return 2*pio2_hi + 0x1p-1000;  /* acos(-1)= pi */
+			return 2*pio2_hi + 0x1p-120f;  /* acos(-1)= pi */
 		}
 		return 0/(x-x);  /* acos(|x|>1) is NaN */
 	}
 	/* |x| < 0.5 */
 	if (expt < 0x3fff - 1) {
 		if (expt < 0x3fff - 65)
-			return pio2_hi + 0x1p-1000;  /* x < 0x1p-65: acosl(x)=pi/2 */
+			return pio2_hi + 0x1p-120f;  /* x < 0x1p-65: acosl(x)=pi/2 */
 		return pio2_hi - (x - (pio2_lo - x * __invtrigl_R(x*x)));
 	}
 	/* x < -0.5 */
diff --git a/src/math/asin.c b/src/math/asin.c
index a1906b08..d61c04b4 100644
--- a/src/math/asin.c
+++ b/src/math/asin.c
@@ -77,14 +77,14 @@ double asin(double x)
 		GET_LOW_WORD(lx, x);
 		if ((ix-0x3ff00000 | lx) == 0)
 			/* asin(1) = +-pi/2 with inexact */
-			return x*pio2_hi + 0x1p-1000;
+			return x*pio2_hi + 0x1p-120f;
 		return 0/(x-x);
 	}
 	/* |x| < 0.5 */
 	if (ix < 0x3fe00000) {
 		if (ix < 0x3e500000) {
 			/* |x|<0x1p-26, return x with inexact if x!=0*/
-			FORCE_EVAL(x + 0x1p1000);
+			FORCE_EVAL(x + 0x1p120f);
 			return x;
 		}
 		return x + x*R(x*x);
diff --git a/src/math/asinh.c b/src/math/asinh.c
index 4152dc39..0829f228 100644
--- a/src/math/asinh.c
+++ b/src/math/asinh.c
@@ -22,7 +22,7 @@ double asinh(double x)
 		x = log1p(x + x*x/(sqrt(x*x+1)+1));
 	} else {
 		/* |x| < 0x1p-26, raise inexact if x != 0 */
-		FORCE_EVAL(x + 0x1p1000);
+		FORCE_EVAL(x + 0x1p120f);
 	}
 	return s ? -x : x;
 }
diff --git a/src/math/asinhl.c b/src/math/asinhl.c
index db966246..3ea88745 100644
--- a/src/math/asinhl.c
+++ b/src/math/asinhl.c
@@ -31,7 +31,7 @@ long double asinhl(long double x)
 		x = log1pl(x + x*x/(sqrtl(x*x+1)+1));
 	} else {
 		/* |x| < 0x1p-32, raise inexact if x!=0 */
-		FORCE_EVAL(x + 0x1p1000);
+		FORCE_EVAL(x + 0x1p120f);
 	}
 	return s ? -x : x;
 }
diff --git a/src/math/asinl.c b/src/math/asinl.c
index 0ef9853b..8799341d 100644
--- a/src/math/asinl.c
+++ b/src/math/asinl.c
@@ -39,13 +39,13 @@ long double asinl(long double x)
 		if (expt == 0x3fff &&
 		    ((u.bits.manh&~LDBL_NBIT)|u.bits.manl) == 0)
 			/* asin(+-1)=+-pi/2 with inexact */
-			return x*pio2_hi + 0x1p-1000;
+			return x*pio2_hi + 0x1p-120f;
 		return 0/(x-x);
 	}
 	if (expt < 0x3fff - 1) {  /* |x| < 0.5 */
 		if (expt < 0x3fff - 32) {  /* |x|<0x1p-32, asinl(x)=x */
 			/* return x with inexact if x!=0 */
-			FORCE_EVAL(x + 0x1p1000);
+			FORCE_EVAL(x + 0x1p120f);
 			return x;
 		}
 		return x + x*__invtrigl_R(x*x);
diff --git a/src/math/atan.c b/src/math/atan.c
index 7fd8a3b8..3c9a59ff 100644
--- a/src/math/atan.c
+++ b/src/math/atan.c
@@ -72,13 +72,13 @@ double atan(double x)
 	if (ix >= 0x44100000) {   /* if |x| >= 2^66 */
 		if (isnan(x))
 			return x;
-		z = atanhi[3] + 0x1p-1000;
+		z = atanhi[3] + 0x1p-120f;
 		return sign ? -z : z;
 	}
 	if (ix < 0x3fdc0000) {    /* |x| < 0.4375 */
 		if (ix < 0x3e400000) {  /* |x| < 2^-27 */
 			/* raise inexact if x!=0 */
-			FORCE_EVAL(x + 0x1p1000);
+			FORCE_EVAL(x + 0x1p120f);
 			return x;
 		}
 		id = -1;
diff --git a/src/math/atan2l.c b/src/math/atan2l.c
index e86dbffb..7cb42c2f 100644
--- a/src/math/atan2l.c
+++ b/src/math/atan2l.c
@@ -52,39 +52,39 @@ long double atan2l(long double y, long double x)
 		switch(m) {
 		case 0:
 		case 1: return y;           /* atan(+-0,+anything)=+-0 */
-		case 2: return  2*pio2_hi+0x1p-1000; /* atan(+0,-anything) = pi */
-		case 3: return -2*pio2_hi-0x1p-1000; /* atan(-0,-anything) =-pi */
+		case 2: return  2*pio2_hi+0x1p-120f; /* atan(+0,-anything) = pi */
+		case 3: return -2*pio2_hi-0x1p-120f; /* atan(-0,-anything) =-pi */
 		}
 	}
 	/* when x = 0 */
 	if (exptx==0 && ((ux.bits.manh&~LDBL_NBIT)|ux.bits.manl)==0)
-		return expsigny < 0 ? -pio2_hi-0x1p-1000 : pio2_hi+0x1p-1000;
+		return expsigny < 0 ? -pio2_hi-0x1p-120f : pio2_hi+0x1p-120f;
 	/* when x is INF */
 	if (exptx == 0x7fff) {
 		if (expty == 0x7fff) {
 			switch(m) {
-			case 0: return  pio2_hi*0.5+0x1p-1000; /* atan(+INF,+INF) */
-			case 1: return -pio2_hi*0.5-0x1p-1000; /* atan(-INF,+INF) */
-			case 2: return  1.5*pio2_hi+0x1p-1000; /* atan(+INF,-INF) */
-			case 3: return -1.5*pio2_hi-0x1p-1000; /* atan(-INF,-INF) */
+			case 0: return  pio2_hi*0.5+0x1p-120f; /* atan(+INF,+INF) */
+			case 1: return -pio2_hi*0.5-0x1p-120f; /* atan(-INF,+INF) */
+			case 2: return  1.5*pio2_hi+0x1p-120f; /* atan(+INF,-INF) */
+			case 3: return -1.5*pio2_hi-0x1p-120f; /* atan(-INF,-INF) */
 			}
 		} else {
 			switch(m) {
 			case 0: return  0.0;        /* atan(+...,+INF) */
 			case 1: return -0.0;        /* atan(-...,+INF) */
-			case 2: return  2*pio2_hi+0x1p-1000; /* atan(+...,-INF) */
-			case 3: return -2*pio2_hi-0x1p-1000; /* atan(-...,-INF) */
+			case 2: return  2*pio2_hi+0x1p-120f; /* atan(+...,-INF) */
+			case 3: return -2*pio2_hi-0x1p-120f; /* atan(-...,-INF) */
 			}
 		}
 	}
 	/* when y is INF */
 	if (expty == 0x7fff)
-		return expsigny < 0 ? -pio2_hi-0x1p-1000 : pio2_hi+0x1p-1000;
+		return expsigny < 0 ? -pio2_hi-0x1p-120f : pio2_hi+0x1p-120f;
 
 	/* compute y/x */
 	k = expty-exptx;
 	if(k > LDBL_MANT_DIG+2) { /* |y/x| huge */
-		z = pio2_hi+0x1p-1000;
+		z = pio2_hi+0x1p-120f;
 		m &= 1;
 	} else if (expsignx < 0 && k < -LDBL_MANT_DIG-2) /* |y/x| tiny, x<0 */
 		z = 0.0;
diff --git a/src/math/atanl.c b/src/math/atanl.c
index 6a480a7a..e76693e4 100644
--- a/src/math/atanl.c
+++ b/src/math/atanl.c
@@ -80,7 +80,7 @@ long double atanl(long double x)
 		if (expt == 0x7fff &&
 		    ((u.bits.manh&~LDBL_NBIT)|u.bits.manl)!=0)  /* NaN */
 			return x+x;
-		z = atanhi[3] + 0x1p-1000;
+		z = atanhi[3] + 0x1p-120f;
 		return expsign < 0 ? -z : z;
 	}
 	/* Extract the exponent and the first few bits of the mantissa. */
@@ -89,7 +89,7 @@ long double atanl(long double x)
 	if (expman < ((0x3fff - 2) << 8) + 0xc0) {  /* |x| < 0.4375 */
 		if (expt < 0x3fff - 32) {   /* if |x| is small, atanl(x)~=x */
 			/* raise inexact if x!=0 */
-			FORCE_EVAL(x + 0x1p1000);
+			FORCE_EVAL(x + 0x1p120f);
 			return x;
 		}
 		id = -1;
diff --git a/src/math/cosh.c b/src/math/cosh.c
index 2fcdea8f..100f8231 100644
--- a/src/math/cosh.c
+++ b/src/math/cosh.c
@@ -1,71 +1,40 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/e_cosh.c */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunSoft, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-/* cosh(x)
- * Method :
- * mathematically cosh(x) if defined to be (exp(x)+exp(-x))/2
- *      1. Replace x by |x| (cosh(x) = cosh(-x)).
- *      2.
- *                                                      [ exp(x) - 1 ]^2
- *          0        <= x <= ln2/2  :  cosh(x) := 1 + -------------------
- *                                                         2*exp(x)
- *
- *                                                exp(x) +  1/exp(x)
- *          ln2/2    <= x <= 22     :  cosh(x) := -------------------
- *                                                        2
- *          22       <= x <= lnovft :  cosh(x) := exp(x)/2
- *          lnovft   <= x <= ln2ovft:  cosh(x) := exp(x/2)/2 * exp(x/2)
- *          ln2ovft  <  x           :  cosh(x) := inf (overflow)
- *
- * Special cases:
- *      cosh(x) is |x| if x is +INF, -INF, or NaN.
- *      only cosh(0)=1 is exact for finite x.
- */
-
 #include "libm.h"
 
+/* cosh(x) = (exp(x) + 1/exp(x))/2
+ *         = 1 + 0.5*(exp(x)-1)*(exp(x)-1)/exp(x)
+ *         = 1 + x*x/2 + o(x^4)
+ */
 double cosh(double x)
 {
 	union {double f; uint64_t i;} u = {.f = x};
-	uint32_t ix;
+	uint32_t w;
 	double t;
 
 	/* |x| */
 	u.i &= (uint64_t)-1/2;
 	x = u.f;
-	ix = u.i >> 32;
+	w = u.i >> 32;
 
-	/* |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) */
-	if (ix < 0x3fd62e43) {
-		t = expm1(x);
-		if (ix < 0x3c800000)
+	/* |x| < log(2) */
+	if (w < 0x3fe62e42) {
+		if (w < 0x3ff00000 - (26<<20)) {
+			/* raise inexact if x!=0 */
+			FORCE_EVAL(x + 0x1p120f);
 			return 1;
+		}
+		t = expm1(x);
 		return 1 + t*t/(2*(1+t));
 	}
 
-	/* |x| in [0.5*ln2,22], return (exp(|x|)+1/exp(|x|))/2; */
-	if (ix < 0x40360000) {
+	/* |x| < log(DBL_MAX) */
+	if (w < 0x40862e42) {
 		t = exp(x);
-		return 0.5*t + 0.5/t;
+		/* note: if x>log(0x1p26) then the 1/t is not needed */
+		return 0.5*(t + 1/t);
 	}
 
-	/* |x| in [22, log(maxdouble)] return 0.5*exp(|x|) */
-	if (ix < 0x40862e42)
-		return 0.5*exp(x);
-
-	/* |x| in [log(maxdouble), overflowthresold] */
-	if (ix <= 0x408633ce)
-		return __expo2(x);
-
-	/* overflow (or nan) */
-	x *= 0x1p1023;
-	return x;
+	/* |x| > log(DBL_MAX) or nan */
+	/* note: the result is stored to handle overflow */
+	t = __expo2(x);
+	return t;
 }
diff --git a/src/math/coshf.c b/src/math/coshf.c
index 2bed1258..b09f2ee5 100644
--- a/src/math/coshf.c
+++ b/src/math/coshf.c
@@ -1,54 +1,33 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/e_coshf.c */
-/*
- * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
- */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-
 #include "libm.h"
 
 float coshf(float x)
 {
 	union {float f; uint32_t i;} u = {.f = x};
-	uint32_t ix;
+	uint32_t w;
 	float t;
 
 	/* |x| */
 	u.i &= 0x7fffffff;
 	x = u.f;
-	ix = u.i;
+	w = u.i;
 
-	/* |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) */
-	if (ix < 0x3eb17218) {
-		t = expm1f(x);
-		if (ix < 0x39800000)
+	/* |x| < log(2) */
+	if (w < 0x3f317217) {
+		if (w < 0x3f800000 - (12<<23)) {
+			FORCE_EVAL(x + 0x1p120f);
 			return 1;
+		}
+		t = expm1f(x);
 		return 1 + t*t/(2*(1+t));
 	}
 
-	/* |x| in [0.5*ln2,9], return (exp(|x|)+1/exp(|x|))/2; */
-	if (ix < 0x41100000) {
+	/* |x| < log(FLT_MAX) */
+	if (w < 0x42b17217) {
 		t = expf(x);
-		return 0.5f*t + 0.5f/t;
+		return 0.5f*(t + 1/t);
 	}
 
-	/* |x| in [9, log(maxfloat)] return 0.5f*exp(|x|) */
-	if (ix < 0x42b17217)
-		return 0.5f*expf(x);
-
-	/* |x| in [log(maxfloat), overflowthresold] */
-	if (ix <= 0x42b2d4fc)
-		return __expo2f(x);
-
-	/* |x| > overflowthresold or nan */
-	x *= 0x1p127f;
-	return x;
+	/* |x| > log(FLT_MAX) or nan */
+	t = __expo2f(x);
+	return t;
 }
diff --git a/src/math/coshl.c b/src/math/coshl.c
index 3ea56e00..d09070bb 100644
--- a/src/math/coshl.c
+++ b/src/math/coshl.c
@@ -1,35 +1,3 @@
-/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_coshl.c */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-/* coshl(x)
- * Method :
- * mathematically coshl(x) if defined to be (exp(x)+exp(-x))/2
- *      1. Replace x by |x| (coshl(x) = coshl(-x)).
- *      2.
- *                                                      [ exp(x) - 1 ]^2
- *          0        <= x <= ln2/2  :  coshl(x) := 1 + -------------------
- *                                                         2*exp(x)
- *
- *                                                 exp(x) +  1/exp(x)
- *          ln2/2    <= x <= 22     :  coshl(x) := -------------------
- *                                                         2
- *          22       <= x <= lnovft :  coshl(x) := expl(x)/2
- *          lnovft   <= x <= ln2ovft:  coshl(x) := expl(x/2)/2 * expl(x/2)
- *          ln2ovft  <  x           :  coshl(x) := inf (overflow)
- *
- * Special cases:
- *      coshl(x) is |x| if x is +INF, -INF, or NaN.
- *      only coshl(0)=1 is exact for finite x.
- */
-
 #include "libm.h"
 
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
@@ -45,41 +13,32 @@ long double coshl(long double x)
 		struct{uint64_t m; uint16_t se; uint16_t pad;} i;
 	} u = {.f = x};
 	unsigned ex = u.i.se & 0x7fff;
+	uint32_t w;
 	long double t;
-	uint32_t mx,lx;
 
 	/* |x| */
 	u.i.se = ex;
 	x = u.f;
-	mx = u.i.m >> 32;
-	lx = u.i.m;
+	w = u.i.m >> 32;
 
-	/* |x| in [0,0.5*ln2], return 1+expm1l(|x|)^2/(2*expl(|x|)) */
-	if (ex < 0x3fff-2 || (ex == 0x3fff-2 && mx < 0xb17217f7)) {
-		t = expm1l(x);
-		if (ex < 0x3fff-64)
+	/* |x| < log(2) */
+	if (ex < 0x3fff-1 || (ex == 0x3fff-1 && w < 0xb17217f7)) {
+		if (ex < 0x3fff-32) {
+			FORCE_EVAL(x + 0x1p120f);
 			return 1;
+		}
+		t = expm1l(x);
 		return 1 + t*t/(2*(1+t));
 	}
 
-	/* |x| in [0.5*ln2,22], return (exp(|x|)+1/exp(|x|)/2; */
-	if (ex < 0x3fff+4 || (ex == 0x3fff+4 && mx < 0xb0000000)) {
+	/* |x| < log(LDBL_MAX) */
+	if (ex < 0x3fff+13 || (ex == 0x3fff+13 && w < 0xb17217f7)) {
 		t = expl(x);
-		return 0.5*t + 0.5/t;
-	}
-
-	/* |x| in [22, ln(maxdouble)] return 0.5*exp(|x|) */
-	if (ex < 0x3fff+13 || (ex == 0x3fff+13 && mx < 0xb1700000))
-		return 0.5*expl(x);
-
-	/* |x| in [log(maxdouble), log(2*maxdouble)) */
-	if (ex == 0x3fff+13 && (mx < 0xb174ddc0 ||
-	     (mx == 0xb174ddc0 && lx < 0x31aec0eb))) {
-		t = expl(0.5*x);
-		return 0.5*t*t;
+		return 0.5*(t + 1/t);
 	}
 
-	/* |x| >= log(2*maxdouble) or nan */
-	return x*0x1p16383L;
+	/* |x| > log(LDBL_MAX) or nan */
+	t = expl(0.5*x);
+	return 0.5*t*t;
 }
 #endif
diff --git a/src/math/exp2f.c b/src/math/exp2f.c
index 279f32de..ea50db4a 100644
--- a/src/math/exp2f.c
+++ b/src/math/exp2f.c
@@ -97,11 +97,11 @@ float exp2f(float x)
 			return x;
 		}
 		if (x >= 128) {
-			STRICT_ASSIGN(float, x, x * 0x1p127);
+			STRICT_ASSIGN(float, x, x * 0x1p127f);
 			return x;
 		}
 		if (x <= -150) {
-			STRICT_ASSIGN(float, x, 0x1p-100*0x1p-100);
+			STRICT_ASSIGN(float, x, 0x1p-100f*0x1p-100f);
 			return x;
 		}
 	} else if (ix <= 0x33000000) {  /* |x| <= 0x1p-25 */
diff --git a/src/math/sinh.c b/src/math/sinh.c
index 0c67ba39..47e36bfa 100644
--- a/src/math/sinh.c
+++ b/src/math/sinh.c
@@ -1,71 +1,39 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/e_sinh.c */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunSoft, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-/* sinh(x)
- * Method :
- * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2
- *      1. Replace x by |x| (sinh(-x) = -sinh(x)).
- *      2.
- *                                                  E + E/(E+1)
- *          0        <= x <= 22     :  sinh(x) := --------------, E=expm1(x)
- *                                                      2
- *
- *          22       <= x <= lnovft :  sinh(x) := exp(x)/2
- *          lnovft   <= x <= ln2ovft:  sinh(x) := exp(x/2)/2 * exp(x/2)
- *          ln2ovft  <  x           :  sinh(x) := x*shuge (overflow)
- *
- * Special cases:
- *      sinh(x) is |x| if x is +INF, -INF, or NaN.
- *      only sinh(0)=0 is exact for finite x.
- */
-
 #include "libm.h"
 
-static const double huge = 1.0e307;
-
+/* sinh(x) = (exp(x) - 1/exp(x))/2
+ *         = (exp(x)-1 + (exp(x)-1)/exp(x))/2
+ *         = x + x^3/6 + o(x^5)
+ */
 double sinh(double x)
 {
-	double t, h;
-	int32_t ix, jx;
-
-	/* High word of |x|. */
-	GET_HIGH_WORD(jx, x);
-	ix = jx & 0x7fffffff;
-
-	/* x is INF or NaN */
-	if (ix >= 0x7ff00000)
-		return x + x;
+	union {double f; uint64_t i;} u = {.f = x};
+	uint32_t w;
+	double t, h, absx;
 
 	h = 0.5;
-	if (jx < 0) h = -h;
-	/* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */
-	if (ix < 0x40360000) {  /* |x|<22 */
-		if (ix < 0x3e300000)  /* |x|<2**-28 */
-			/* raise inexact, return x */
-			if (huge+x > 1.0)
+	if (u.i >> 63)
+		h = -h;
+	/* |x| */
+	u.i &= (uint64_t)-1/2;
+	absx = u.f;
+	w = u.i >> 32;
+
+	/* |x| < log(DBL_MAX) */
+	if (w < 0x40862e42) {
+		t = expm1(absx);
+		if (w < 0x3ff00000) {
+			if (w < 0x3ff00000 - (26<<20))
+				/* note: inexact is raised by expm1 */
+				/* note: this branch avoids underflow */
 				return x;
-		t = expm1(fabs(x));
-		if (ix < 0x3ff00000)
-			return h*(2.0*t - t*t/(t+1.0));
-		return h*(t + t/(t+1.0));
+			return h*(2*t - t*t/(t+1));
+		}
+		/* note: |x|>log(0x1p26)+eps could be just h*exp(x) */
+		return h*(t + t/(t+1));
 	}
 
-	/* |x| in [22, log(maxdouble)] return 0.5*exp(|x|) */
-	if (ix < 0x40862E42)
-		return h*exp(fabs(x));
-
-	/* |x| in [log(maxdouble), overflowthresold] */
-	if (ix <= 0x408633CE)
-		return h * 2.0 * __expo2(fabs(x)); /* h is for sign only */
-
-	/* |x| > overflowthresold, sinh(x) overflow */
-	return x*huge;
+	/* |x| > log(DBL_MAX) or nan */
+	/* note: the result is stored to handle overflow */
+	t = 2*h*__expo2(absx);
+	return t;
 }
diff --git a/src/math/sinhf.c b/src/math/sinhf.c
index b8d88224..6ad19ea2 100644
--- a/src/math/sinhf.c
+++ b/src/math/sinhf.c
@@ -1,57 +1,31 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/e_sinhf.c */
-/*
- * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
- */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-
 #include "libm.h"
 
-static const float huge = 1.0e37;
-
 float sinhf(float x)
 {
-	float t, h;
-	int32_t ix, jx;
-
-	GET_FLOAT_WORD(jx, x);
-	ix = jx & 0x7fffffff;
-
-	/* x is INF or NaN */
-	if (ix >= 0x7f800000)
-		return x + x;
+	union {float f; uint32_t i;} u = {.f = x};
+	uint32_t w;
+	float t, h, absx;
 
 	h = 0.5;
-	if (jx < 0)
+	if (u.i >> 31)
 		h = -h;
-	/* |x| in [0,9], return sign(x)*0.5*(E+E/(E+1))) */
-	if (ix < 0x41100000) {   /* |x|<9 */
-		if (ix < 0x39800000)  /* |x|<2**-12 */
-			/* raise inexact, return x */
-			if (huge+x > 1.0f)
+	/* |x| */
+	u.i &= 0x7fffffff;
+	absx = u.f;
+	w = u.i;
+
+	/* |x| < log(FLT_MAX) */
+	if (w < 0x42b17217) {
+		t = expm1f(absx);
+		if (w < 0x3f800000) {
+			if (w < 0x3f800000 - (12<<23))
 				return x;
-		t = expm1f(fabsf(x));
-		if (ix < 0x3f800000)
-			return h*(2.0f*t - t*t/(t+1.0f));
-		return h*(t + t/(t+1.0f));
+			return h*(2*t - t*t/(t+1));
+		}
+		return h*(t + t/(t+1));
 	}
 
-	/* |x| in [9, logf(maxfloat)] return 0.5*exp(|x|) */
-	if (ix < 0x42b17217)
-		return h*expf(fabsf(x));
-
-	/* |x| in [logf(maxfloat), overflowthresold] */
-	if (ix <= 0x42b2d4fc)
-		return h * 2.0f * __expo2f(fabsf(x)); /* h is for sign only */
-
-	/* |x| > overflowthresold, sinh(x) overflow */
-	return x*huge;
+	/* |x| > logf(FLT_MAX) or nan */
+	t = 2*h*__expo2f(absx);
+	return t;
 }
diff --git a/src/math/sinhl.c b/src/math/sinhl.c
index 8a54677e..14e3371b 100644
--- a/src/math/sinhl.c
+++ b/src/math/sinhl.c
@@ -1,32 +1,3 @@
-/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_sinhl.c */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-/* sinhl(x)
- * Method :
- * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2
- *      1. Replace x by |x| (sinhl(-x) = -sinhl(x)).
- *      2.
- *                                                   E + E/(E+1)
- *          0        <= x <= 25     :  sinhl(x) := --------------, E=expm1l(x)
- *                                                       2
- *
- *          25       <= x <= lnovft :  sinhl(x) := expl(x)/2
- *          lnovft   <= x <= ln2ovft:  sinhl(x) := expl(x/2)/2 * expl(x/2)
- *          ln2ovft  <  x           :  sinhl(x) := x*huge (overflow)
- *
- * Special cases:
- *      sinhl(x) is |x| if x is +INF, -INF, or NaN.
- *      only sinhl(0)=0 is exact for finite x.
- */
-
 #include "libm.h"
 
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
@@ -35,47 +6,35 @@ long double sinhl(long double x)
 	return sinh(x);
 }
 #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
-static const long double huge = 1.0e4931L;
-
 long double sinhl(long double x)
 {
-	long double t,w,h;
-	uint32_t jx,ix,i0,i1;
-
-	/* Words of |x|. */
-	GET_LDOUBLE_WORDS(jx, i0, i1, x);
-	ix = jx & 0x7fff;
-
-	/* x is INF or NaN */
-	if (ix == 0x7fff) return x + x;
+	union {
+		long double f;
+		struct{uint64_t m; uint16_t se; uint16_t pad;} i;
+	} u = {.f = x};
+	unsigned ex = u.i.se & 0x7fff;
+	long double h, t, absx;
 
 	h = 0.5;
-	if (jx & 0x8000)
+	if (u.i.se & 0x8000)
 		h = -h;
-	/* |x| in [0,25], return sign(x)*0.5*(E+E/(E+1))) */
-	if (ix < 0x4003 || (ix == 0x4003 && i0 <= 0xc8000000)) { /* |x| < 25 */
-		if (ix < 0x3fdf)  /* |x|<2**-32 */
-			if (huge + x > 1.0)
-				return x;/* sinh(tiny) = tiny with inexact */
-		t = expm1l(fabsl(x));
-		if (ix < 0x3fff)
-			return h*(2.0*t - t*t/(t + 1.0));
-		return h*(t + t/(t + 1.0));
-	}
-
-	/* |x| in [25, log(maxdouble)] return 0.5*exp(|x|) */
-	if (ix < 0x400c || (ix == 0x400c && i0 < 0xb17217f7))
-		return h*expl(fabsl(x));
-
-	/* |x| in [log(maxdouble), overflowthreshold] */
-	if (ix < 0x400c || (ix == 0x400c && (i0 < 0xb174ddc0 ||
-	     (i0 == 0xb174ddc0 && i1 <= 0x31aec0ea)))) {
-		w = expl(0.5*fabsl(x));
-		t = h*w;
-		return t*w;
+	/* |x| */
+	u.i.se = ex;
+	absx = u.f;
+
+	/* |x| < log(LDBL_MAX) */
+	if (ex < 0x3fff+13 || (ex == 0x3fff+13 && u.i.m>>32 < 0xb17217f7)) {
+		t = expm1l(absx);
+		if (ex < 0x3fff) {
+			if (ex < 0x3fff-32)
+				return x;
+			return h*(2*t - t*t/(1+t));
+		}
+		return h*(t + t/(t+1));
 	}
 
-	/* |x| > overflowthreshold, sinhl(x) overflow */
-	return x*huge;
+	/* |x| > log(LDBL_MAX) or nan */
+	t = expl(0.5*absx);
+	return h*t*t;
 }
 #endif
diff --git a/src/math/tanh.c b/src/math/tanh.c
index 21138643..0e766c5c 100644
--- a/src/math/tanh.c
+++ b/src/math/tanh.c
@@ -1,73 +1,41 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_tanh.c */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-/* Tanh(x)
- * Return the Hyperbolic Tangent of x
- *
- * Method :
- *                                     x    -x
- *                                    e  - e
- *      0. tanh(x) is defined to be -----------
- *                                     x    -x
- *                                    e  + e
- *      1. reduce x to non-negative by tanh(-x) = -tanh(x).
- *      2.  0      <= x <  2**-28 : tanh(x) := x with inexact if x != 0
- *                                              -t
- *          2**-28 <= x <  1      : tanh(x) := -----; t = expm1(-2x)
- *                                             t + 2
- *                                                   2
- *          1      <= x <  22     : tanh(x) := 1 - -----; t = expm1(2x)
- *                                                 t + 2
- *          22     <= x <= INF    : tanh(x) := 1.
- *
- * Special cases:
- *      tanh(NaN) is NaN;
- *      only tanh(0)=0 is exact for finite argument.
- */
-
 #include "libm.h"
 
-static const double tiny = 1.0e-300, huge = 1.0e300;
-
+/* tanh(x) = (exp(x) - exp(-x))/(exp(x) + exp(-x))
+ *         = (exp(2*x) - 1)/(exp(2*x) - 1 + 2)
+ *         = (1 - exp(-2*x))/(exp(-2*x) - 1 + 2)
+ */
 double tanh(double x)
 {
-	double t,z;
-	int32_t jx,ix;
-
-	GET_HIGH_WORD(jx, x);
-	ix = jx & 0x7fffffff;
+	union {double f; uint64_t i;} u = {.f = x};
+	uint32_t w;
+	int sign;
+	double t;
 
-	/* x is INF or NaN */
-	if (ix >= 0x7ff00000) {
-		if (jx >= 0)
-			return 1.0f/x + 1.0f;  /* tanh(+-inf)=+-1 */
-		else
-			return 1.0f/x - 1.0f;  /* tanh(NaN) = NaN */
-	}
+	/* x = |x| */
+	sign = u.i >> 63;
+	u.i &= (uint64_t)-1/2;
+	x = u.f;
+	w = u.i >> 32;
 
-	if (ix < 0x40360000) {  /* |x| < 22 */
-		if (ix < 0x3e300000) {  /* |x| < 2**-28 */
-			/* tanh(tiny) = tiny with inexact */
-			if (huge+x > 1.0f)
-				return x;
-		}
-		if (ix >= 0x3ff00000) {  /* |x| >= 1  */
-			t = expm1(2.0f*fabs(x));
-			z = 1.0f - 2.0f/(t+2.0f);
+	if (w > 0x3fe193ea) {
+		/* |x| > log(3)/2 ~= 0.5493 or nan */
+		if (w > 0x40340000) {
+			/* |x| > 20 or nan */
+			/* note: this branch avoids raising overflow */
+			/* raise inexact if x!=+-inf and handle nan */
+			t = 1 + 0/(x + 0x1p-120f);
 		} else {
-			t = expm1(-2.0f*fabs(x));
-			z= -t/(t+2.0f);
+			t = expm1(2*x);
+			t = 1 - 2/(t+2);
 		}
-	} else {  /* |x| >= 22, return +-1 */
-		z = 1.0f - tiny;  /* raise inexact */
+	} else if (w > 0x3fd058ae) {
+		/* |x| > log(5/3)/2 ~= 0.2554 */
+		t = expm1(2*x);
+		t = t/(t+2);
+	} else {
+		/* |x| is small, up to 2ulp error in [0.1,0.2554] */
+		t = expm1(-2*x);
+		t = -t/(t+2);
 	}
-	return jx >= 0 ? z : -z;
+	return sign ? -t : t;
 }
diff --git a/src/math/tanhf.c b/src/math/tanhf.c
index 7cb459d0..8099ec30 100644
--- a/src/math/tanhf.c
+++ b/src/math/tanhf.c
@@ -1,55 +1,35 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_tanhf.c */
-/*
- * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
- */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-
 #include "libm.h"
 
-static const float
-tiny = 1.0e-30,
-huge = 1.0e30;
-
 float tanhf(float x)
 {
-	float t,z;
-	int32_t jx,ix;
+	union {float f; uint32_t i;} u = {.f = x};
+	uint32_t w;
+	int sign;
+	float t;
 
-	GET_FLOAT_WORD(jx, x);
-	ix = jx & 0x7fffffff;
+	/* x = |x| */
+	sign = u.i >> 31;
+	u.i &= 0x7fffffff;
+	x = u.f;
+	w = u.i;
 
-	/* x is INF or NaN */
-	if(ix >= 0x7f800000) {
-		if (jx >= 0)
-			return 1.0f/x + 1.0f;  /* tanh(+-inf)=+-1 */
-		else
-			return 1.0f/x - 1.0f;  /* tanh(NaN) = NaN */
-	}
-
-	if (ix < 0x41100000) {  /* |x| < 9 */
-		if (ix < 0x39800000) {  /* |x| < 2**-12 */
-			/* tanh(tiny) = tiny with inexact */
-			if (huge+x > 1.0f)
-				return x;
-		}
-		if (ix >= 0x3f800000) {  /* |x|>=1  */
-			t = expm1f(2.0f*fabsf(x));
-			z = 1.0f - 2.0f/(t+2.0f);
+	if (w > 0x3f0c9f54) {
+		/* |x| > log(3)/2 ~= 0.5493 or nan */
+		if (w > 0x41200000) {
+			/* |x| > 10 */
+			t = 1 + 0/(x + 0x1p-120f);
 		} else {
-			t = expm1f(-2.0f*fabsf(x));
-			z = -t/(t+2.0f);
+			t = expm1f(2*x);
+			t = 1 - 2/(t+2);
 		}
-	} else {  /* |x| >= 9, return +-1 */
-		z = 1.0f - tiny;  /* raise inexact */
+	} else if (w > 0x3e82c578) {
+		/* |x| > log(5/3)/2 ~= 0.2554 */
+		t = expm1f(2*x);
+		t = t/(t+2);
+	} else {
+		/* |x| is small */
+		t = expm1f(-2*x);
+		t = -t/(t+2);
 	}
-	return jx >= 0 ? z : -z;
+	return sign ? -t : t;
 }
diff --git a/src/math/tanhl.c b/src/math/tanhl.c
index 92efb20d..66559e9f 100644
--- a/src/math/tanhl.c
+++ b/src/math/tanhl.c
@@ -1,38 +1,3 @@
-/* origin: OpenBSD /usr/src/lib/libm/src/ld80/s_tanhl.c */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-/* tanhl(x)
- * Return the Hyperbolic Tangent of x
- *
- * Method :
- *                                      x    -x
- *                                     e  - e
- *      0. tanhl(x) is defined to be -----------
- *                                      x    -x
- *                                     e  + e
- *      1. reduce x to non-negative by tanhl(-x) = -tanhl(x).
- *      2.  0      <= x <= 2**-55 : tanhl(x) := x*(one+x)
- *                                               -t
- *          2**-55 <  x <=  1     : tanhl(x) := -----; t = expm1l(-2x)
- *                                              t + 2
- *                                                    2
- *          1      <= x <=  23.0  : tanhl(x) := 1-  ----- ; t=expm1l(2x)
- *                                                  t + 2
- *          23.0   <  x <= INF    : tanhl(x) := 1.
- *
- * Special cases:
- *      tanhl(NaN) is NaN;
- *      only tanhl(0)=0 is exact for finite argument.
- */
-
 #include "libm.h"
 
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
@@ -41,43 +6,40 @@ long double tanhl(long double x)
 	return tanh(x);
 }
 #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
-static const long double tiny = 1.0e-4900L;
-
 long double tanhl(long double x)
 {
-	long double t,z;
-	int32_t se;
-	uint32_t jj0,jj1,ix;
+	union {
+		long double f;
+		struct{uint64_t m; uint16_t se; uint16_t pad;} i;
+	} u = {.f = x};
+	unsigned ex = u.i.se & 0x7fff;
+	unsigned sign = u.i.se & 0x8000;
+	uint32_t w;
+	long double t;
 
-	/* High word of |x|. */
-	GET_LDOUBLE_WORDS(se, jj0, jj1, x);
-	ix = se & 0x7fff;
-
-	/* x is INF or NaN */
-	if (ix == 0x7fff) {
-		/* for NaN it's not important which branch: tanhl(NaN) = NaN */
-		if (se & 0x8000)
-			return 1.0/x-1.0;  /* tanhl(-inf)= -1; */
-		return 1.0/x+1.0;          /* tanhl(+inf)= +1 */
-	}
+	/* x = |x| */
+	u.i.se = ex;
+	x = u.f;
+	w = u.i.m >> 32;
 
-	/* |x| < 23 */
-	if (ix < 0x4003 || (ix == 0x4003 && jj0 < 0xb8000000u)) {
-		if ((ix|jj0|jj1) == 0) /* x == +- 0 */
-			return x;
-		if (ix < 0x3fc8)       /* |x| < 2**-55 */
-			return x*(1.0+tiny);  /* tanh(small) = small */
-		if (ix >= 0x3fff) {    /* |x| >= 1  */
-			t = expm1l(2.0*fabsl(x));
-			z = 1.0 - 2.0/(t+2.0);
+	if (ex > 0x3ffe || (ex == 0x3ffe && w > 0x8c9f53d5)) {
+		/* |x| > log(3)/2 ~= 0.5493 or nan */
+		if (ex >= 0x3fff+5) {
+			/* |x| >= 32 */
+			t = 1 + 0/(x + 0x1p-120f);
 		} else {
-			t = expm1l(-2.0*fabsl(x));
-			z = -t/(t+2.0);
+			t = expm1l(2*x);
+			t = 1 - 2/(t+2);
 		}
-	/* |x| > 23, return +-1 */
+	} else if (ex > 0x3ffd || (ex == 0x3ffd && w > 0x82c577d4)) {
+		/* |x| > log(5/3)/2 ~= 0.2554 */
+		t = expm1l(2*x);
+		t = t/(t+2);
 	} else {
-		z = 1.0 - tiny;  /* raise inexact flag */
+		/* |x| is small */
+		t = expm1l(-2*x);
+		t = -t/(t+2);
 	}
-	return se & 0x8000 ? -z : z;
+	return sign ? -t : t;
 }
 #endif
diff --git a/src/math/tgammal.c b/src/math/tgammal.c
index 86782a96..5c1a02a6 100644
--- a/src/math/tgammal.c
+++ b/src/math/tgammal.c
@@ -205,42 +205,36 @@ static long double stirf(long double x)
 long double tgammal(long double x)
 {
 	long double p, q, z;
-	int i, sign;
 
-	if (isnan(x))
-		return NAN;
-	if (x == INFINITY)
-		return INFINITY;
-	if (x == -INFINITY)
-		return x - x;
+	if (!isfinite(x))
+		return x + INFINITY;
+
 	q = fabsl(x);
 	if (q > 13.0) {
-		sign = 1;
-		if (q > MAXGAML)
-			goto goverf;
 		if (x < 0.0) {
 			p = floorl(q);
-			if (p == q)
-				return (x - x) / (x - x);
-			i = p;
-			if ((i & 1) == 0)
-				sign = -1;
 			z = q - p;
-			if (z > 0.5L) {
-				p += 1.0;
-				z = q - p;
-			}
-			z = q * sinl(PIL * z);
-			z = fabsl(z) * stirf(q);
-			if (z <= PIL/LDBL_MAX) {
-goverf:
-				return sign * INFINITY;
+			if (z == 0)
+				return 0 / z;
+			if (q > MAXGAML) {
+				z = 0;
+			} else {
+				if (z > 0.5) {
+					p += 1.0;
+					z = q - p;
+				}
+				z = q * sinl(PIL * z);
+				z = fabsl(z) * stirf(q);
+				z = PIL/z;
 			}
-			z = PIL/z;
+			if (0.5 * p == floorl(q * 0.5))
+				z = -z;
+		} else if (x > MAXGAML) {
+			z = x * 0x1p16383L;
 		} else {
 			z = stirf(x);
 		}
-		return sign * z;
+		return z;
 	}
 
 	z = 1.0;
@@ -268,8 +262,9 @@ goverf:
 	return z;
 
 small:
-	if (x == 0.0)
-		return (x - x) / (x - x);
+	/* z==1 if x was originally +-0 */
+	if (x == 0 && z != 1)
+		return x / x;
 	if (x < 0.0) {
 		x = -x;
 		q = z / (x * __polevll(x, SN, 8));