#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 [8,2]-degree rational minimax polynomial
     * computed in Maple by
     *
     *     with(numapprox):
     *     Digits := 30:
     *     minimax((exp(x)-1)/x, x = ln(1/2)..ln(3/2), [8,2], 1, 'err');
     *     printf("%.2e\n", err);
     *
     * The reported absolute error is 4.46e-17, 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 *
	    ( (0.972760965309405311622155720413e+00 +
	       (0.307787828517447727384884772352e+00 +
		(0.818657798196538507995540410710e-01 +
		 (0.152839042000987783573773325934e-01 +
		  (0.217086067815840319776222026328e-02 +
		   (0.239254749790356045755155302865e-03 +
		    (0.202567665043690345972014473134e-04 +
		     (0.123997064618335651098991731845e-05 +
		      0.440230411775549299639177088088e-07 *
		      x) * x) * x) * x) * x) * x) * x) * x) /
	      (0.972760965309405292862421978316e+00 +
	       (-0.178592654137254098688418858833e+00 +
		0.903527933671750503621315618349e-02 * x) * x) );
    }
    else
	ret_val = EXP(x) - ONE;

    return (ret_val);
}
