#include "mymath.h"

#define EXP(x)	(expf(x))

float
expm1f(float x)
{
    /*
     * (expm1)
     * Return (exp(x) - 1), taking care to avoid subtraction loss.
     *
     * This version uses a [2,2]-degree rational minimax polynomial
     * computed in Maple by
     *
     *     with(numapprox):
     *     Digits := 20:
     *     minimax((exp(x)-1-x)/x^2, x = ln(1/2)..ln(3/2), [2,2], 1, 'err');
     *     printf("%.2e\n", err);
     *
     * The reported absolute error is 7.38e-09, below the IEEE 754
     * machine epsilon of 2**(-23) = 1.19e-07.  This is the lowest
     * degree, and thus optimal, minimax polynomial whose error is below
     * that limit.
     * (28-Jun-2002)
     */

    float ret_val;

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

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

    static const float ONE = 1.0F;
    static const float ZERO = 0.0F;

    /*
     *
     *     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.74231896e-01F +
			     ( -1.71277179e-03F +
			       (  2.48343063e-03F
				  ) * x) * x)
			  /
			  (  9.48463848e-01F +
			     ( -3.19579791e-01F +
			       (  3.24522081e-02F
				  ) * x) * x) ) );
    }
    else
	ret_val = EXP(x) - ONE;

    return (ret_val);
}
