From e42a977fe5dbe48ba45072ab82886e6b5a694487 Mon Sep 17 00:00:00 2001 From: Szabolcs Nagy Date: Sun, 16 Dec 2012 19:52:42 +0100 Subject: math: tanh.c cleanup similar to sinh, cosh comments are kept in the double version of the function compared to fdlibm/freebsd we partition the domain into one more part and select different threshold points: now the [log(5/3)/2,log(3)/2] and [log(3)/2,inf] domains should have <1.5ulp error (so only the last bit may be wrong, assuming good exp, expm1) (note that log(3)/2 and log(5/3)/2 are the points where tanh changes resolution: tanh(log(3)/2)=0.5, tanh(log(5/3)/2)=0.25) for some x < log(5/3)/2 (~=0.2554) the error can be >1.5ulp but it should be <2ulp (the freebsd code had some >2ulp errors in [0.255,1]) even with the extra logic the new code produces smaller object files --- src/math/tanhf.c | 70 ++++++++++++++++++++------------------------------------ 1 file changed, 25 insertions(+), 45 deletions(-) (limited to 'src/math/tanhf.c') 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; } -- cgit v1.2.1