about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSzabolcs Nagy <nsz@port70.net>2013-05-18 14:40:22 +0000
committerSzabolcs Nagy <nsz@port70.net>2013-05-18 14:40:22 +0000
commitbfda37935867f9bf271d6074db0accf05c63ad10 (patch)
tree92d7fffcc6fb7fd9caad6f3bde14dcd768d32b66 /src
parent1d5ba3bb5a3f55e10db05219638cfcd967d65417 (diff)
downloadmusl-bfda37935867f9bf271d6074db0accf05c63ad10.tar.gz
musl-bfda37935867f9bf271d6074db0accf05c63ad10.tar.xz
musl-bfda37935867f9bf271d6074db0accf05c63ad10.zip
math: sin cos cleanup
* use unsigned arithmetics
* use unsigned to store arg reduction quotient (so n&3 is understood)
* remove z=0.0 variables, use literal 0
* raise underflow and inexact exceptions properly when x is small
* fix spurious underflow in tanl
Diffstat (limited to 'src')
-rw-r--r--src/math/cos.c20
-rw-r--r--src/math/cosf.c33
-rw-r--r--src/math/cosl.c22
-rw-r--r--src/math/sin.c15
-rw-r--r--src/math/sincos.c12
-rw-r--r--src/math/sincosf.c45
-rw-r--r--src/math/sincosl.c20
-rw-r--r--src/math/sinf.c33
-rw-r--r--src/math/sinl.c29
-rw-r--r--src/math/tanl.c11
10 files changed, 128 insertions, 112 deletions
diff --git a/src/math/cos.c b/src/math/cos.c
index 76990e7f..ee97f68b 100644
--- a/src/math/cos.c
+++ b/src/math/cos.c
@@ -44,26 +44,28 @@
 
 double cos(double x)
 {
-	double y[2],z=0.0;
-	int32_t n, ix;
+	double y[2];
+	uint32_t ix;
+	unsigned n;
 
 	GET_HIGH_WORD(ix, x);
+	ix &= 0x7fffffff;
 
 	/* |x| ~< pi/4 */
-	ix &= 0x7fffffff;
 	if (ix <= 0x3fe921fb) {
-		if (ix < 0x3e46a09e)  /* if x < 2**-27 * sqrt(2) */
-			/* raise inexact if x != 0 */
-			if ((int)x == 0)
-				return 1.0;
-		return __cos(x, z);
+		if (ix < 0x3e46a09e) {  /* |x| < 2**-27 * sqrt(2) */
+			/* raise inexact if x!=0 */
+			FORCE_EVAL(x + 0x1p120f);
+			return 1.0;
+		}
+		return __cos(x, 0);
 	}
 
 	/* cos(Inf or NaN) is NaN */
 	if (ix >= 0x7ff00000)
 		return x-x;
 
-	/* argument reduction needed */
+	/* argument reduction */
 	n = __rem_pio2(x, y);
 	switch (n&3) {
 	case 0: return  __cos(y[0], y[1]);
diff --git a/src/math/cosf.c b/src/math/cosf.c
index 4d94130f..23f3e5bf 100644
--- a/src/math/cosf.c
+++ b/src/math/cosf.c
@@ -26,34 +26,39 @@ c4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */
 float cosf(float x)
 {
 	double y;
-	int32_t n, hx, ix;
+	uint32_t ix;
+	unsigned n, sign;
+
+	GET_FLOAT_WORD(ix, x);
+	sign = ix >> 31;
+	ix &= 0x7fffffff;
 
-	GET_FLOAT_WORD(hx, x);
-	ix = hx & 0x7fffffff;
 	if (ix <= 0x3f490fda) {  /* |x| ~<= pi/4 */
-		if (ix < 0x39800000)  /* |x| < 2**-12 */
-			if ((int)x == 0)  /* raise inexact if x != 0 */
-				return 1.0;
+		if (ix < 0x39800000) {  /* |x| < 2**-12 */
+			/* raise inexact if x != 0 */
+			FORCE_EVAL(x + 0x1p120f);
+			return 1.0f;
+		}
 		return __cosdf(x);
 	}
 	if (ix <= 0x407b53d1) {  /* |x| ~<= 5*pi/4 */
 		if (ix > 0x4016cbe3)  /* |x|  ~> 3*pi/4 */
-			return -__cosdf(hx > 0 ? x-c2pio2 : x+c2pio2);
+			return -__cosdf(sign ? x+c2pio2 : x-c2pio2);
 		else {
-			if (hx > 0)
-				return __sindf(c1pio2 - x);
-			else
+			if (sign)
 				return __sindf(x + c1pio2);
+			else
+				return __sindf(c1pio2 - x);
 		}
 	}
 	if (ix <= 0x40e231d5) {  /* |x| ~<= 9*pi/4 */
 		if (ix > 0x40afeddf)  /* |x| ~> 7*pi/4 */
-			return __cosdf(hx > 0 ? x-c4pio2 : x+c4pio2);
+			return __cosdf(sign ? x+c4pio2 : x-c4pio2);
 		else {
-			if (hx > 0)
-				return __sindf(x - c3pio2);
+			if (sign)
+				return __sindf(-x - c3pio2);
 			else
-				return __sindf(-c3pio2 - x);
+				return __sindf(x - c3pio2);
 		}
 	}
 
diff --git a/src/math/cosl.c b/src/math/cosl.c
index 25d9005a..0794d284 100644
--- a/src/math/cosl.c
+++ b/src/math/cosl.c
@@ -39,30 +39,30 @@ long double cosl(long double x) {
 long double cosl(long double x)
 {
 	union IEEEl2bits z;
-	int e0;
+	unsigned n;
 	long double y[2];
 	long double hi, lo;
 
 	z.e = x;
 	z.bits.sign = 0;
 
-	/* If x = +-0 or x is a subnormal number, then cos(x) = 1 */
-	if (z.bits.exp == 0)
-		return 1.0;
-
 	/* If x = NaN or Inf, then cos(x) = NaN. */
-	if (z.bits.exp == 32767)
+	if (z.bits.exp == 0x7fff)
 		return (x - x) / (x - x);
 
-	/* Optimize the case where x is already within range. */
-	if (z.e < M_PI_4)
+	/* |x| < (double)pi/4 */
+	if (z.e < M_PI_4) {
+		/* |x| < 0x1p-64 */
+		if (z.bits.exp < 0x3fff - 64)
+			/* raise inexact if x!=0 */
+			return 1.0 + x;
 		return __cosl(z.e, 0);
+	}
 
-	e0 = __rem_pio2l(x, y);
+	n = __rem_pio2l(x, y);
 	hi = y[0];
 	lo = y[1];
-
-	switch (e0 & 3) {
+	switch (n & 3) {
 	case 0:
 		hi = __cosl(hi, lo);
 		break;
diff --git a/src/math/sin.c b/src/math/sin.c
index 8e430f85..055e215b 100644
--- a/src/math/sin.c
+++ b/src/math/sin.c
@@ -44,21 +44,22 @@
 
 double sin(double x)
 {
-	double y[2], z=0.0;
-	int32_t n, ix;
+	double y[2];
+	uint32_t ix;
+	unsigned n;
 
 	/* High word of x. */
 	GET_HIGH_WORD(ix, x);
+	ix &= 0x7fffffff;
 
 	/* |x| ~< pi/4 */
-	ix &= 0x7fffffff;
 	if (ix <= 0x3fe921fb) {
 		if (ix < 0x3e500000) {  /* |x| < 2**-26 */
-			/* raise inexact if x != 0 */
-			if ((int)x == 0)
-				return x;
+			/* raise inexact if x != 0 and underflow if subnormal*/
+			FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f);
+			return x;
 		}
-		return __sin(x, z, 0);
+		return __sin(x, 0.0, 0);
 	}
 
 	/* sin(Inf or NaN) is NaN */
diff --git a/src/math/sincos.c b/src/math/sincos.c
index 442e285e..49f8a098 100644
--- a/src/math/sincos.c
+++ b/src/math/sincos.c
@@ -15,7 +15,8 @@
 void sincos(double x, double *sin, double *cos)
 {
 	double y[2], s, c;
-	uint32_t n, ix;
+	uint32_t ix;
+	unsigned n;
 
 	GET_HIGH_WORD(ix, x);
 	ix &= 0x7fffffff;
@@ -24,11 +25,10 @@ void sincos(double x, double *sin, double *cos)
 	if (ix <= 0x3fe921fb) {
 		/* if |x| < 2**-27 * sqrt(2) */
 		if (ix < 0x3e46a09e) {
-			/* raise inexact if x != 0 */
-			if ((int)x == 0) {
-				*sin = x;
-				*cos = 1.0;
-			}
+			/* raise inexact if x!=0 and underflow if subnormal */
+			FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f);
+			*sin = x;
+			*cos = 1.0;
 			return;
 		}
 		*sin = __sin(x, 0.0, 0);
diff --git a/src/math/sincosf.c b/src/math/sincosf.c
index 5e3b9a41..1b50f01c 100644
--- a/src/math/sincosf.c
+++ b/src/math/sincosf.c
@@ -25,21 +25,23 @@ s4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */
 
 void sincosf(float x, float *sin, float *cos)
 {
-	double y, s, c;
-	uint32_t n, hx, ix;
+	double y;
+	float_t s, c;
+	uint32_t ix;
+	unsigned n, sign;
 
-	GET_FLOAT_WORD(hx, x);
-	ix = hx & 0x7fffffff;
+	GET_FLOAT_WORD(ix, x);
+	sign = ix >> 31;
+	ix &= 0x7fffffff;
 
 	/* |x| ~<= pi/4 */
 	if (ix <= 0x3f490fda) {
 		/* |x| < 2**-12 */
 		if (ix < 0x39800000) {
-			/* raise inexact if x != 0 */
-			if((int)x == 0) {
-				*sin = x;
-				*cos = 1.0f;
-			}
+			/* raise inexact if x!=0 and underflow if subnormal */
+			FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f);
+			*sin = x;
+			*cos = 1.0f;
 			return;
 		}
 		*sin = __sindf(x);
@@ -50,34 +52,35 @@ void sincosf(float x, float *sin, float *cos)
 	/* |x| ~<= 5*pi/4 */
 	if (ix <= 0x407b53d1) {
 		if (ix <= 0x4016cbe3) {  /* |x| ~<= 3pi/4 */
-			if (hx < 0x80000000) {
-				*sin = __cosdf(x - s1pio2);
-				*cos = __sindf(s1pio2 - x);
-			} else {
+			if (sign) {
 				*sin = -__cosdf(x + s1pio2);
 				*cos = __sindf(x + s1pio2);
+			} else {
+				*sin = __cosdf(s1pio2 - x);
+				*cos = __sindf(s1pio2 - x);
 			}
 			return;
 		}
-		*sin = __sindf(hx < 0x80000000 ? s2pio2 - x : -s2pio2 - x);
-		*cos = -__cosdf(hx < 0x80000000 ? x - s2pio2 : x + s2pio2);
+		/* -sin(x+c) is not correct if x+c could be 0: -0 vs +0 */
+		*sin = -__sindf(sign ? x + s2pio2 : x - s2pio2);
+		*cos = -__cosdf(sign ? x + s2pio2 : x - s2pio2);
 		return;
 	}
 
 	/* |x| ~<= 9*pi/4 */
 	if (ix <= 0x40e231d5) {
 		if (ix <= 0x40afeddf) {  /* |x| ~<= 7*pi/4 */
-			if (hx < 0x80000000) {
+			if (sign) {
+				*sin = __cosdf(x + s3pio2);
+				*cos = -__sindf(x + s3pio2);
+			} else {
 				*sin = -__cosdf(x - s3pio2);
 				*cos = __sindf(x - s3pio2);
-			} else {
-				*sin = __cosdf(x + s3pio2);
-				*cos = __sindf(-s3pio2 - x);
 			}
 			return;
 		}
-		*sin = __sindf(hx < 0x80000000 ? x - s4pio2 : x + s4pio2);
-		*cos = __cosdf(hx < 0x80000000 ? x - s4pio2 : x + s4pio2);
+		*sin = __sindf(sign ? x + s4pio2 : x - s4pio2);
+		*cos = __cosdf(sign ? x + s4pio2 : x - s4pio2);
 		return;
 	}
 
diff --git a/src/math/sincosl.c b/src/math/sincosl.c
index d632fe6f..5db69bd6 100644
--- a/src/math/sincosl.c
+++ b/src/math/sincosl.c
@@ -10,27 +10,29 @@ void sincosl(long double x, long double *sin, long double *cos)
 void sincosl(long double x, long double *sin, long double *cos)
 {
 	union IEEEl2bits u;
-	int n;
+	unsigned n;
 	long double y[2], s, c;
 
 	u.e = x;
 	u.bits.sign = 0;
 
-	/* x = +-0 or subnormal */
-	if (!u.bits.exp) {
-		*sin = x;
-		*cos = 1.0;
-		return;
-	}
-
 	/* x = nan or inf */
 	if (u.bits.exp == 0x7fff) {
 		*sin = *cos = x - x;
 		return;
 	}
 
-	/* |x| < pi/4 */
+	/* |x| < (double)pi/4 */
 	if (u.e < M_PI_4) {
+		/* |x| < 0x1p-64 */
+		if (u.bits.exp < 0x3fff - 64) {
+			/* raise underflow if subnormal */
+			if (u.bits.exp == 0) FORCE_EVAL(x*0x1p-120f);
+			*sin = x;
+			/* raise inexact if x!=0 */
+			*cos = 1.0 + x;
+			return;
+		}
 		*sin = __sinl(x, 0, 0);
 		*cos = __cosl(x, 0);
 		return;
diff --git a/src/math/sinf.c b/src/math/sinf.c
index dcca67af..64e39f50 100644
--- a/src/math/sinf.c
+++ b/src/math/sinf.c
@@ -26,35 +26,38 @@ s4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */
 float sinf(float x)
 {
 	double y;
-	int32_t n, hx, ix;
+	uint32_t ix;
+	int n, sign;
 
-	GET_FLOAT_WORD(hx, x);
-	ix = hx & 0x7fffffff;
+	GET_FLOAT_WORD(ix, x);
+	sign = ix >> 31;
+	ix &= 0x7fffffff;
 
 	if (ix <= 0x3f490fda) {  /* |x| ~<= pi/4 */
-		if (ix < 0x39800000)  /* |x| < 2**-12 */
-			/* raise inexact if x != 0 */
-			if((int)x == 0)
-				return x;
+		if (ix < 0x39800000) {  /* |x| < 2**-12 */
+			/* raise inexact if x!=0 and underflow if subnormal */
+			FORCE_EVAL(ix < 0x00800000 ? x/0x1p120f : x+0x1p120f);
+			return x;
+		}
 		return __sindf(x);
 	}
 	if (ix <= 0x407b53d1) {  /* |x| ~<= 5*pi/4 */
 		if (ix <= 0x4016cbe3) {  /* |x| ~<= 3pi/4 */
-			if (hx > 0)
-				return __cosdf(x - s1pio2);
-			else
+			if (sign)
 				return -__cosdf(x + s1pio2);
+			else
+				return __cosdf(x - s1pio2);
 		}
-		return __sindf(hx > 0 ? s2pio2 - x : -s2pio2 - x);
+		return __sindf(sign ? -(x + s2pio2) : -(x - s2pio2));
 	}
 	if (ix <= 0x40e231d5) {  /* |x| ~<= 9*pi/4 */
 		if (ix <= 0x40afeddf) {  /* |x| ~<= 7*pi/4 */
-			if (hx > 0)
-				return -__cosdf(x - s3pio2);
-			else
+			if (sign)
 				return __cosdf(x + s3pio2);
+			else
+				return -__cosdf(x - s3pio2);
 		}
-		return __sindf(hx > 0 ? x - s4pio2 : x + s4pio2);
+		return __sindf(sign ? x + s4pio2 : x - s4pio2);
 	}
 
 	/* sin(Inf or NaN) is NaN */
diff --git a/src/math/sinl.c b/src/math/sinl.c
index 7e0b44f4..6ca99986 100644
--- a/src/math/sinl.c
+++ b/src/math/sinl.c
@@ -37,33 +37,32 @@ long double sinl(long double x)
 long double sinl(long double x)
 {
 	union IEEEl2bits z;
-	int e0, s;
+	unsigned n;
 	long double y[2];
 	long double hi, lo;
 
 	z.e = x;
-	s = z.bits.sign;
 	z.bits.sign = 0;
 
-	/* If x = +-0 or x is a subnormal number, then sin(x) = x */
-	if (z.bits.exp == 0)
-		return x;
-
 	/* If x = NaN or Inf, then sin(x) = NaN. */
-	if (z.bits.exp == 32767)
+	if (z.bits.exp == 0x7fff)
 		return (x - x) / (x - x);
 
-	/* Optimize the case where x is already within range. */
+	/* |x| < (double)pi/4 */
 	if (z.e < M_PI_4) {
-		hi = __sinl(z.e, 0, 0);
-		return  s ? -hi : hi;
+		/* |x| < 0x1p-64 */
+		if (z.bits.exp < 0x3fff - 64) {
+			/* raise inexact if x!=0 and underflow if subnormal */
+			FORCE_EVAL(z.bits.exp == 0 ? x/0x1p120f : x+0x1p120f);
+			return x;
+		}
+		return __sinl(x, 0.0, 0);
 	}
 
-	e0 = __rem_pio2l(x, y);
+	n = __rem_pio2l(x, y);
 	hi = y[0];
 	lo = y[1];
-
-	switch (e0 & 3) {
+	switch (n & 3) {
 	case 0:
 		hi = __sinl(hi, lo, 1);
 		break;
@@ -71,10 +70,10 @@ long double sinl(long double x)
 		hi = __cosl(hi, lo);
 		break;
 	case 2:
-		hi = - __sinl(hi, lo, 1);
+		hi = -__sinl(hi, lo, 1);
 		break;
 	case 3:
-		hi = - __cosl(hi, lo);
+		hi = -__cosl(hi, lo);
 		break;
 	}
 	return hi;
diff --git a/src/math/tanl.c b/src/math/tanl.c
index 3b51e011..546c7a02 100644
--- a/src/math/tanl.c
+++ b/src/math/tanl.c
@@ -53,11 +53,12 @@ long double tanl(long double x)
 
 	/* |x| < (double)pi/4 */
 	if (z.e < M_PI_4) {
-		/* x = +-0 or x is subnormal */
-		if (z.bits.exp == 0)
-			/* inexact and underflow if x!=0 */
-			return x + x*0x1p-120f;
-		/* can raise spurious underflow */
+		/* |x| < 0x1p-64 */
+		if (z.bits.exp < 0x3fff - 64) {
+			/* raise inexact if x!=0 and underflow if subnormal */
+			FORCE_EVAL(z.bits.exp == 0 ? x/0x1p120f : x+0x1p120f);
+			return x;
+		}
 		return __tanl(x, 0, 0);
 	}