diff options
author | Joseph Myers <joseph@codesourcery.com> | 2012-03-16 20:05:04 +0000 |
---|---|---|
committer | Joseph Myers <joseph@codesourcery.com> | 2012-03-16 20:05:37 +0000 |
commit | 11b90b9f504df5b2da91ce3a06c1657d99e4a95f (patch) | |
tree | 838df025f383c47096e24ac377aa45d48d9200b7 /sysdeps/ieee754 | |
parent | 6a1bd2a100c958d30bbfe8c9b8f9071d24b7c3f4 (diff) | |
download | glibc-11b90b9f504df5b2da91ce3a06c1657d99e4a95f.tar.gz glibc-11b90b9f504df5b2da91ce3a06c1657d99e4a95f.tar.xz glibc-11b90b9f504df5b2da91ce3a06c1657d99e4a95f.zip |
Fix tan, tanl for large inputs.
Diffstat (limited to 'sysdeps/ieee754')
-rw-r--r-- | sysdeps/ieee754/dbl-64/s_tan.c | 4 | ||||
-rw-r--r-- | sysdeps/ieee754/ldbl-96/k_tanl.c | 137 | ||||
-rw-r--r-- | sysdeps/ieee754/ldbl-96/s_tanl.c | 11 |
3 files changed, 147 insertions, 5 deletions
diff --git a/sysdeps/ieee754/dbl-64/s_tan.c b/sysdeps/ieee754/dbl-64/s_tan.c index acff67c987..8eee383933 100644 --- a/sysdeps/ieee754/dbl-64/s_tan.c +++ b/sysdeps/ieee754/dbl-64/s_tan.c @@ -74,7 +74,7 @@ tan(double x) { int __branred(double, double *, double *); int __mpranred(double, mp_no *, int); - libc_feholdexcept_setround (&env, FE_TONEAREST); + libc_feholdexcept_setround_53bit (&env, FE_TONEAREST); /* x=+-INF, x=NaN */ num.d = x; ux = num.i[HIGH_HALF]; @@ -503,7 +503,7 @@ tan(double x) { goto ret; ret: - libc_feupdateenv (&env); + libc_feupdateenv_53bit (&env); return retval; } diff --git a/sysdeps/ieee754/ldbl-96/k_tanl.c b/sysdeps/ieee754/ldbl-96/k_tanl.c new file mode 100644 index 0000000000..31cd236aa2 --- /dev/null +++ b/sysdeps/ieee754/ldbl-96/k_tanl.c @@ -0,0 +1,137 @@ +/* + * ==================================================== + * 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. + * ==================================================== + */ + +/* + Long double expansions are + Copyright (C) 2001 Stephen L. Moshier <moshier@na-net.ornl.gov> + and are incorporated herein by permission of the author. The author + reserves the right to distribute this material elsewhere under different + copying permissions. These modifications are distributed here under + the following terms: + + This 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. + + This 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 this library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* __kernel_tanl( x, y, k ) + * kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input k indicates whether tan (if k=1) or + * -1/tan (if k= -1) is returned. + * + * Algorithm + * 1. Since tan(-x) = -tan(x), we need only to consider positive x. + * 2. if x < 2^-33, return x with inexact if x!=0. + * 3. tan(x) is approximated by a rational form x + x^3 / 3 + x^5 R(x^2) + * on [0,0.67433]. + * + * Note: tan(x+y) = tan(x) + tan'(x)*y + * ~ tan(x) + (1+x*x)*y + * Therefore, for better accuracy in computing tan(x+y), let + * r = x^3 * R(x^2) + * then + * tan(x+y) = x + (x^3 / 3 + (x^2 *(r+y)+y)) + * + * 4. For x in [0.67433,pi/4], let y = pi/4 - x, then + * tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) + * = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) + */ + +#include <math.h> +#include <math_private.h> +static const long double + one = 1.0L, + pio4hi = 0xc.90fdaa22168c235p-4L, + pio4lo = -0x3.b399d747f23e32ecp-68L, + + /* tan x = x + x^3 / 3 + x^5 T(x^2)/U(x^2) + 0 <= x <= 0.6743316650390625 + Peak relative error 8.0e-36 */ + TH = 3.333333333333333333333333333333333333333E-1L, + T0 = -1.813014711743583437742363284336855889393E7L, + T1 = 1.320767960008972224312740075083259247618E6L, + T2 = -2.626775478255838182468651821863299023956E4L, + T3 = 1.764573356488504935415411383687150199315E2L, + T4 = -3.333267763822178690794678978979803526092E-1L, + + U0 = -1.359761033807687578306772463253710042010E8L, + U1 = 6.494370630656893175666729313065113194784E7L, + U2 = -4.180787672237927475505536849168729386782E6L, + U3 = 8.031643765106170040139966622980914621521E4L, + U4 = -5.323131271912475695157127875560667378597E2L; + /* 1.000000000000000000000000000000000000000E0 */ + + +long double +__kernel_tanl (long double x, long double y, int iy) +{ + long double z, r, v, w, s; + long double absx = fabsl (x); + int sign; + + if (absx < 0x1p-33) + { + if ((int) x == 0) + { /* generate inexact */ + if (x == 0 && iy == -1) + return one / fabsl (x); + else + return (iy == 1) ? x : -one / x; + } + } + if (absx >= 0.6743316650390625L) + { + if (signbit (x)) + { + x = -x; + y = -y; + sign = -1; + } + else + sign = 1; + z = pio4hi - x; + w = pio4lo - y; + x = z + w; + y = 0.0; + } + z = x * x; + r = T0 + z * (T1 + z * (T2 + z * (T3 + z * T4))); + v = U0 + z * (U1 + z * (U2 + z * (U3 + z * (U4 + z)))); + r = r / v; + + s = z * x; + r = y + z * (s * r + y); + r += TH * s; + w = x + r; + if (absx >= 0.6743316650390625L) + { + v = (long double) iy; + w = (v - 2.0 * (x - (w * w / (w + v) - r))); + if (sign < 0) + w = -w; + return w; + } + if (iy == 1) + return w; + else + return -1.0 / (x + r); +} diff --git a/sysdeps/ieee754/ldbl-96/s_tanl.c b/sysdeps/ieee754/ldbl-96/s_tanl.c index 3054601a50..3fbe4a8f6b 100644 --- a/sysdeps/ieee754/ldbl-96/s_tanl.c +++ b/sysdeps/ieee754/ldbl-96/s_tanl.c @@ -48,23 +48,28 @@ static char rcsid[] = "$NetBSD: $"; * TRIG(x) returns trig(x) nearly rounded */ +#include <errno.h> #include <math.h> #include <math_private.h> long double __tanl(long double x) { long double y[2],z=0.0; - int32_t n, se; + int32_t n, se, i0, i1; /* High word of x. */ - GET_LDOUBLE_EXP(se,x); + GET_LDOUBLE_WORDS(se,i0,i1,x); /* |x| ~< pi/4 */ se &= 0x7fff; if(se <= 0x3ffe) return __kernel_tanl(x,z,1); /* tan(Inf or NaN) is NaN */ - else if (se==0x7fff) return x-x; /* NaN */ + else if (se==0x7fff) { + if (i1 == 0 && i0 == 0x80000000) + __set_errno (EDOM); + return x-x; + } /* argument reduction needed */ else { |