#include "mymath.h"

#define EXP(x)	(exp(x))

double
expm1(double x)
{
    /*
     * (expm1)
     * Return (exp(x) - 1), taking care to avoid subtraction loss.
     *
     * This version uses a [4,7]-degree Padé approximation computed in
     * Maple by
     *
     *     with(numapprox):
     *     Digits := 20:
     *     pade((exp('x') - 1 - 'x')/'x'^2, 'x' = (ln(1/2)+ln(3/2))/2, [4,7]);
     *
     * The computed relative error is 4.68e-18 at x = 0.4055, below
     * the IEEE 754 machine epsilon of 2**(-52) = 2.22e-16.  This is
     * the lowest degree, and thus optimal, Padé approximation whose
     * error is below that limit.
     * (29-Jun-2002)
     */

    double ret_val;

    /*
     *
     * CUTLO = ln(0.5) (really, ln(1-1/beta) for arbitrary base beta)
     *
     */
    static const double CUTLO = -0.6931471805599453094172321214581765680755001343603;

    /*
     *
     * CUTHI = ln(1.5) (really, ln(1+1/beta) for arbitrary base beta)
     *
     */
    static const double CUTHI = 0.4054651081081643819780131154643491365719904234625;

    static const double ONE = 1.0;
    static const double ZERO = 0.0;

    /*
     *
     *     We handle the computation in three regions:
     *
     *     x in [-Infinity, CUTLO):  exp(x) - 1
     *     x in [CUTLO, CUTHI]:      Padé approximation
     *     x in (CUTHI, Infinity]:   exp(x) - 1
     *
     *     The central region suffers loss of one or more bits if the
     *     simple formula is used.
     *
     *     The following IF statements handle the case of NaN, signed zero,
     *     and the three regions above.
     *
     */

    if (x != x)				/* then x is a NaN */
	ret_val = x;
    else if (x == ZERO)			/* then x is +0 or -0 */
	ret_val = x;			/* preserve sign of zero */
    else if (x < CUTLO)
	ret_val = EXP(x) - ONE;
    else if (x <= CUTHI)		/* region of accuracy loss from exp(x)-1 */
          ret_val = x + 
	      (x * x * 
	       (
		(  4.6969426863446711e-01 +
		   ( -4.8268560688693705e-02 +
		     (  1.0918155925073419e-02 +
			( -3.9886659000102040e-04 +
			  (  3.3477940659025022e-05
			     ) * x) * x) * x) * x)
		/
		(  9.3938853726893422e-01 +
		   ( -4.0966663380036548e-01 +
		     (  8.0109478344524148e-02 +
			( -9.0184820992952034e-03 +
			  (  6.1569134552071340e-04 +
			     ( -2.3658603381660569e-05 +
			       (  3.2976614358014640e-07 +
				  (  5.3290461261147527e-09
				     ) * x) * x) * x) * x) * x) * x) * x) ) );
    else
	ret_val = EXP(x) - ONE;

    return (ret_val);
}
