This is the mail archive of the libc-alpha@sources.redhat.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

tanhl patch



For very small x, tanh(x) => x.
The long double version of the hyperbolic tangent copies some
code from the double precision version that does this for x < 2^-55.
The threshold value is ok, but the code is not.  Instead of x, it returns
x * (1 + x).  The purpose of multiplying by 1 + x is to set the inexact
flag, while 1 + x is supposed to be equal to 1.  But in long double 
that isn't equal to one, so the incorrect outcome is that tanh(x) != x.
An extremely small number called tiny is already defined, so a simple fix
is to return x * (1 + tiny) for this case.

Here is a test program:

/* 2^-56 */
long double x = 1.387778780781445675529539585113525390625e-17L;
long double tanhl (long double);

main()
{
  long double y = tanhl (x);
  if (y != x)
    abort ();
  exit (0);
}


	* sysdeps/ieee754/ldbl-96/s_tanhl.c (__tanhl): Make sure
	  result equals argument when x < 2^-55.

*** sysdeps/ieee754/ldbl-96/s_tanhl.c	Tue Jul 13 20:15:23 1999
--- ./s_tanhl.c	Mon Jun 18 20:29:48 2001
*************** static long double one=1.0, two=2.0, tin
*** 78,84 ****
  	    if ((ix|j0|j1) == 0)
  		return x;		/* x == +- 0 */
  	    if (ix<0x3fc8) 		/* |x|<2**-55 */
! 		return x*(one+x);    	/* tanh(small) = small */
  	    if (ix>=0x3fff) {	/* |x|>=1  */
  		t = __expm1l(two*fabsl(x));
  		z = one - two/(t+two);
--- 78,84 ----
  	    if ((ix|j0|j1) == 0)
  		return x;		/* x == +- 0 */
  	    if (ix<0x3fc8) 		/* |x|<2**-55 */
! 		return x*(one+tiny);   	/* tanh(small) = small */
  	    if (ix>=0x3fff) {	/* |x|>=1  */
  		t = __expm1l(two*fabsl(x));
  		z = one - two/(t+two);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]