188 lines
4.5 KiB
C
188 lines
4.5 KiB
C
|
/***
|
||
|
*exp.c - exponential
|
||
|
*
|
||
|
* Copyright (c) 1991-2001, Microsoft Corporation. All rights reserved.
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Compute exp(x)
|
||
|
*
|
||
|
*Revision History:
|
||
|
* 8-15-91 GDP written
|
||
|
* 12-21-91 GDP support IEEE exceptions
|
||
|
* 02-03-92 GDP added _exphlp for use by exp, sinh, and cosh
|
||
|
* 02-06-95 JWM Mac merge
|
||
|
* 10-07-97 RDL Added IA64.
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
#include <math.h>
|
||
|
#include <trans.h>
|
||
|
|
||
|
#if defined(_M_IA64)
|
||
|
#pragma function(exp)
|
||
|
#endif
|
||
|
|
||
|
double _exphlp(double, int *);
|
||
|
|
||
|
/*
|
||
|
* Thresholds for over/underflow that results in an adjusted value
|
||
|
* too big/small to be represented as a double.
|
||
|
* OVFX: ln(XMAX * 2^IEEE_ADJ)
|
||
|
* UFLX: ln(XIN * 2^(-IEEE_ADJ)
|
||
|
*/
|
||
|
|
||
|
static _dbl const ovfx = {SET_DBL(0x40862e42, 0xfefa39f8)}; /* 709.782712893385 */
|
||
|
static _dbl const uflx = {SET_DBL(0xc086232b, 0xdd7abcda)}; /* -708.396418532265 */
|
||
|
|
||
|
#define OVFX ovfx.dbl
|
||
|
#define UFLX uflx.dbl
|
||
|
|
||
|
|
||
|
static double const EPS = 5.16987882845642297e-26; /* 2^(-53) / 2 */
|
||
|
static double const LN2INV = 1.442695040889634074; /* 1/ln(2) */
|
||
|
static double const C1 = 0.693359375000000000;
|
||
|
static double const C2 = -2.1219444005469058277e-4;
|
||
|
|
||
|
/* constants for the rational approximation */
|
||
|
static double const p0 = 0.249999999999999993e+0;
|
||
|
static double const p1 = 0.694360001511792852e-2;
|
||
|
static double const p2 = 0.165203300268279130e-4;
|
||
|
static double const q0 = 0.500000000000000000e+0;
|
||
|
static double const q1 = 0.555538666969001188e-1;
|
||
|
static double const q2 = 0.495862884905441294e-3;
|
||
|
|
||
|
#define P(z) ( (p2 * (z) + p1) * (z) + p0 )
|
||
|
#define Q(z) ( (q2 * (z) + q1) * (z) + q0 )
|
||
|
|
||
|
/***
|
||
|
*double exp(double x) - exponential
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Compute the exponential of a number.
|
||
|
* The algorithm (reduction / rational approximation) is
|
||
|
* taken from Cody & Waite.
|
||
|
*
|
||
|
*Entry:
|
||
|
*
|
||
|
*Exit:
|
||
|
*
|
||
|
*Exceptions: O, U, P, I
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
double exp (double x)
|
||
|
{
|
||
|
uintptr_t savedcw;
|
||
|
int newexp;
|
||
|
double result;
|
||
|
|
||
|
/* save user fp control word */
|
||
|
savedcw = _maskfp();
|
||
|
|
||
|
if (IS_D_SPECIAL(x)){
|
||
|
switch (_sptype(x)) {
|
||
|
case T_PINF:
|
||
|
RETURN(savedcw,x);
|
||
|
case T_NINF:
|
||
|
RETURN(savedcw,0.0);
|
||
|
case T_QNAN:
|
||
|
return _handle_qnan1(OP_EXP, x, savedcw);
|
||
|
default: //T_SNAN
|
||
|
return _except1(FP_I, OP_EXP, x, _s2qnan(x), savedcw);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (x == 0.0) {
|
||
|
RETURN(savedcw, 1.0);
|
||
|
}
|
||
|
|
||
|
if (x > OVFX) {
|
||
|
|
||
|
// even after scaling the exponent of the result,
|
||
|
// it is still too large.
|
||
|
// Deliver infinity to the trap handler
|
||
|
|
||
|
return _except1(FP_O | FP_P, OP_EXP, x, D_INF, savedcw);
|
||
|
}
|
||
|
|
||
|
if (x < UFLX) {
|
||
|
|
||
|
// even after scaling the exponent of the result,
|
||
|
// it is still too small.
|
||
|
// Deliver 0 to the trap handler
|
||
|
|
||
|
return _except1(FP_U | FP_P, OP_EXP, x, 0.0, savedcw);
|
||
|
}
|
||
|
|
||
|
if (ABS(x) < EPS) {
|
||
|
result = 1.0;
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
result = _exphlp(x, &newexp);
|
||
|
if (newexp > MAXEXP) {
|
||
|
result = _set_exp(result, newexp-IEEE_ADJUST);
|
||
|
return _except1(FP_O | FP_P, OP_EXP, x, result, savedcw);
|
||
|
}
|
||
|
else if (newexp < MINEXP) {
|
||
|
result = _set_exp(result, newexp+IEEE_ADJUST);
|
||
|
return _except1(FP_U | FP_P, OP_EXP, x, result, savedcw);
|
||
|
}
|
||
|
else
|
||
|
result = _set_exp(result, newexp);
|
||
|
}
|
||
|
|
||
|
RETURN_INEXACT1(OP_EXP, x, result, savedcw);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***
|
||
|
*double _exphlp(double x, int * pnewexp) - exp helper routine
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Provide the mantissa and the exponent of e^x
|
||
|
*
|
||
|
*Entry:
|
||
|
* x : a (non special) double precision number
|
||
|
*
|
||
|
*Exit:
|
||
|
* *newexp: the exponent of e^x
|
||
|
* return value: the mantissa m of e^x scaled by a factor
|
||
|
* (the value of this factor has no significance.
|
||
|
* The mantissa can be obtained with _set_exp(m, 0).
|
||
|
*
|
||
|
* _set_exp(m, *pnewexp) may be used for constructing the final
|
||
|
* result, if it is within the representable range.
|
||
|
*
|
||
|
*Exceptions:
|
||
|
* No exceptions are raised by this function
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
double _exphlp(double x, int * pnewexp)
|
||
|
{
|
||
|
|
||
|
double xn;
|
||
|
double g,z,gpz,qz,rg;
|
||
|
int n;
|
||
|
|
||
|
xn = _frnd(x * LN2INV);
|
||
|
n = (int) xn;
|
||
|
|
||
|
/* assume guard digit is present */
|
||
|
g = (x - xn * C1) - xn * C2;
|
||
|
z = g*g;
|
||
|
gpz = g * P(z);
|
||
|
qz = Q(z);
|
||
|
rg = 0.5 + gpz/(qz-gpz);
|
||
|
|
||
|
n++;
|
||
|
|
||
|
*pnewexp = _get_exp(rg) + n;
|
||
|
return rg;
|
||
|
}
|