#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 [2,7]-degree rational minimax polynomial
     * computed in Maple by
     *
     *     with(numapprox):
     *     Digits := 60:
     *     minimax((exp(x)-1-x)/x^2, x = ln(1/2)..ln(3/2), [2,7], 1, 'err');
     *     printf("%.2e\n", err);
     *
     * The reported absolute error is 4.67e-18, below the IEEE 754
     * machine epsilon of 2**(-52) = 2.22e-16.  This is the lowest
     * degree, and thus optimal, minimax polynomial whose error is below
     * that limit.
     */

    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]:      minimax rational polynomial
     *     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.6725946288043156e-01 +
		(  3.2851354138135712e-02 +
		   ( -7.7147172221884448e-03
		     ) * x) * x)
	      /
	      ( -9.3451892576086312e-01 +
		(  3.7720901686322570e-01 +
		   ( -6.3289196252045559e-02 +
		     (  5.2376294414332701e-03 +
			( -1.6270783567142839e-04 +
			  ( -4.3752185414398622e-06 +
			    (  1.9587930519571511e-07 +
			       (  1.6698937101747154e-08
				  ) * x) * x) * x) * x) * x) * x) * x) ) );
    }
    else
	ret_val = EXP(x) - ONE;

    return (ret_val);
}
