#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 incorporates argument range reduction to shorten
     * the number of Taylor series terms needed, as well as run-time
     * precomputation of a table of reciprocals to replace division by
     * multiplication in the Taylor series computation.
     * (11-Jun-2002)
     */

    float ret_val,
        sum,
        term,
        xhalf;
    int n;

    /*
     * The maximum number of Taylor series terms (10, 17, 19, 29) is
     * known in advance, but we compute 10 more reciprocals for safety.
     */
#define MAXN_	39
    static const int MAXN = MAXN_;
    static float xninv[MAXN_];
    static int first = 1;

    /*
     *
     * 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 HALF = 0.5F;
    static const float ONE = 1.0F;
    static const float ZERO = 0.0F;

    if (first)			/* precompute reciprocal table */
    {
	for (n = 0; n < MAXN; ++n)
	    xninv[n] = ONE / (float)(n + 1);
	first = 0;
    }

    /*
     *
     *     We handle the computation in three regions:
     *
     *     x in [-Infinity, CUTLO):  exp(x) - 1
     *     x in [CUTLO, CUTHI]:      Taylor series
     *     x in (CUTHI, Infinity]:   exp(x) - 1
     *
     *     The central region suffers loss of one or more bits if the
     *     simple formula is used.
     *
     *     The number of terms in the Taylor series depends on the value of
     *     x, and the arithmetic system: these values are for IEEE 754
     *     arithmetic:
     *
     *                 =========================================
     *                             Taylor series terms needed
     *                 x         32-bit  64-bit  80-bit  128-bit
     *                 =========================================
     *                 CUTLO      10      17      19       29
     *                 CUTLO/2     8      14      16       24
     *                 0           1       1       1        1
     *                 0.001       4       6       7       11
     *                 0.01        4       7       9       13
     *                 0.1         6      11      12       19
     *                 CUTHI/2     7      12      14       22
     *                 CUTHI       8      14      16       25
     *                 =========================================
     *
     *     By using the argument reduction formula
     *
     *         expm1(x) = (exp(x) - 1)
     *                   = (exp(x/2)*exp(x/2) - 1)
     *                   = (expm1(x/2)+1)^2 - 1)
     *                   = expm1(x/2)^2 + 2*expm1(x/2)
     *
     *     we can compute the Taylor series for x/2, then recover expm1(x)
     *     with two multiplies and two adds.  Each Taylor series term costs
     *     one add, one multiply, and one divide, and the above table
     *     suggests that halving x saves two Taylor series terms.  The
     *     reduction formula therefore saves two divides, which is
     *     substantial.  However, this reduction should be done ONLY on
     *     base-2 floating-point systems; on IBM S/390 base-16 systems,
     *     division by 2 loses one bit.
     *
     *     The following IF statements handle the case of NaN, signed zero,
     *     and the three regions above.  If NaN comparisons are botched or
     *     incorrectly optimized away, code should fall through to the ELSE
     *     part, which will generate a NaN provided that exp(NaN) returns a
     *     NaN.  If NaN's comparisons are badly botched, then we might get
     *     into a loop that never terminates: therefore, validation testing
     *     is imperative!
     *
     */

    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 */
    {
	xhalf = x * HALF;
	term = xhalf;
	sum = ZERO;
	n = 1;
	while ((sum + term) != sum)	/* Taylor series summation */
	{
	    sum = sum + term;
	    term = term * xhalf * xninv[n];
	    n++;
	}
	ret_val = sum * sum + sum + sum;
    }
    else
	ret_val = EXP(x) - ONE;

    return (ret_val);
}
