/*------------------------------------------------------------------------
 * filename - tanh.cas
 *
 * function(s)
 *        tanh - hyperbolic tangent function
 *-----------------------------------------------------------------------*/

/*
 *      C/C++ Run Time Library - Version 5.0
 *
 *      Copyright (c) 1987, 1992 by Borland International
 *      All Rights Reserved.
 *
 */


#pragma inline
#include <asmrules.h>

#include <_math.h>
#include <math.h>


/*--------------------------------------------------------------------------*

Name            tanh - hyperbolic tangent function

Usage           double tanh(double x);

Prototype in    math.h

Description     tanh calculates the hyperbolic tangent of the input.

Return value    tanh returns the hyperbolic tangent of the input value.
                For large arguments (magnitude greater than 32) the result
                will be +1.0 or -1.0.

*---------------------------------------------------------------------------*/

/*
Algorithm.

The usual formula is:
        tanh(x) = (exp(x) - exp(-x))/(exp(x) + exp(-x))/
but there is a loss of precision in using this formula directly near 0.

Since tanh(-x) = -tanh(x), compute tanh(|x|) and adjust the sign later.

If 0 <= x < 2^-33, return x.

If x >= 32 return 1.

If x >= .17325, use
        y = exp(x)
        tanh(x) = (y - 1/y)/(y + 1/y)

If 2^-33 <= x < .17325, use
        y = exp(2x) - 1
        sinh(x) = y/(2 + y)
where special chip functions are used to get exp(2x)-1 accurately.

*/

#pragma warn -rvl
double _FARFUNC tanh( double  x )
{
asm     FLD     DOUBLE (x)
asm     sub     dh, dh
asm     mov     cx, x [6]
asm     shl     cx, 1
asm     rcr     dh, 1                   /* DH = sign    */
asm     cmp     cx, 8080h
asm     FABS
asm     ja      tanh_extreme

asm     cmp     cx, 7F8Ch
asm     jb      tanh_small

#ifdef _Windows
        _f87_Exp();
#else
asm     _FAST_  (_FEXP_)                /* Exp (x)      */
#endif

asm     FLD1
asm     FDIV    st, st(1)               /* Exp (-x)     */

/*
  tanh = sinh / cosh = (exp(x) - exp(-x)) / (exp(x) + exp(-x))
*/
asm     FLD     st(1)
asm     FSUB    st, st(1)
asm     FXCH
asm     FADD    st, st(2)
asm     FDIVP   st(1), st
asm     FSTP    st(1)

tanh_end:
asm     or      dh, dh
asm     jns     tanh_end2
asm     FCHS
tanh_end2:
        return;

/*
  tanh is asymptotic to -1 for negative arguments and +1 for positives.
  It approaches very fast, with exponentially increasing accuracy,
  so it is 1.0 for IEEE accuracy when |x| > 23.
*/
tanh_extreme:
asm     FSTP    st(0)           /* pop stack    */
asm     FLD1
        goto tanh_end;

tanh_small:
asm     cmp     cx, 7BC0h
asm     jb      tanh_end
asm     FLD1
asm     FXCH
asm     FSCALE

asm     FLDL2E
asm     FMUL
asm     F2XM1
/* TOS = y = exp(2x) - 1 */
asm     FXCH
asm     FLD1
asm     FADD
asm     FADD    st(0),st(1)
/* stack = 2+y,y */
asm     FDIV
        goto tanh_end;
}
#pragma warn .rvl
