EXP(3) | Library Functions Manual | EXP(3) |
exp
, exp2
,
expm1
— exponential
functions
#include
<math.h>
float
expf
(float
x);
double
exp
(double
x);
long double
expl
(long
double x);
float
exp2f
(float
x);
double
exp2
(double
x);
long double
exp2l
(long
double x);
float
expm1f
(float
x);
double
expm1
(double
x);
long double
expm1l
(long
double x);
float
__exp10f
(float
x);
double
__exp10
(double
x);
The
exp
()
function computes e**x, the base-e exponential of
x.
The
exp2
()
function computes 2**x, the base-2 exponential of
x.
The
__exp10
()
function computes 10**x; it is not defined in the C standard, and therefore
may not be portable to other platforms. It is provided as a convenience to
programmers because it may be computed more efficiently than pow(10,x).
If x is nearly zero, then the
common expression
exp
(x)
- 1.0 will suffer from catastrophic cancellation and the result will have
little or no precision. The expm1
() function
provides an alternative means to do this calculation without the risk of
significant loss of precision. If you find yourself using this function, you
are likely to also be interested in the
log1p
()
function.
Note that computations numerically equivalent to
exp
(x)
- 1.0 are often hidden in more complicated expressions; some amount of
algebraic manipulation may be necessary to take advantage of the
expm1
() function. Consider the following example,
abstracted from a developer's actual production code in a bug report:
double z = exp(-x/y)*(x*x/y/y + 2*x/y + 2) - 2
When x is small relative to y, this expression is approximately equal to:
double z = 2*(exp(-x/y) - 1)
and all precision of the result is lost in the
computation due to catastrophic cancellation. The developer was aware that
they were losing precision, but didn't know what to do about it. To remedy
the situation, we do a little algebra and re-write the expression to take
advantage of the
expm1
()
function:
exp(-x/y)*(x*x/y/y + 2*x/y + 2) - 2 = (2*exp(-x/y) - 2) + exp(-x/y)*((x*x)/(y*y) + 2*x/y)
This transformation allows the result to be computed to a high degree of accuracy as follows:
const double r = x/y; const double emrm1 = expm1(-r); double z = 2.0*emrm1 + (1.0 + emrm1)*(2.0 + r)*r;
It is not always easy to spot such opportunities for
improvement; if an expression involving
exp
() seems
to be suffering from an undue loss of accuracy, try a few simple algebraic
operations to see if you can identify a factor with the form
exp
(x) - 1.0, and substitute
expm1
(x) in its place.
exp
(±0) and
exp2
(±0) return 1.
exp
(-infinity)
and exp2
(-infinity) return
+0.
exp
(+infinity)
and exp2
(+infinity) return
+infinity.
expm1
(±0)
returns ±0.
expm1
(-infinity)
returns -1.
expm1
(+infinity)
returns +infinity.
For all these functions, a range error occurs if the magnitude of x is too large.
If you need to apply the
exp
()
functions to SIMD vectors or arrays, using the following functions provided
by the Accelerate.framework may give significantly better performance:
#include
<Accelerate/Accelerate.h>
vFloat
vexpf
(vFloat
x);
vFloat
vexpm1f
(vFloat
x);
void
vvexpf
(float
*y, const float *x, const int
*n);
void
vvexp
(double
*y, const double *x, const int
*n);
void
vvexpm1f
(float
*y, const float *x, const int
*n);
void
vvexpm1
(double
*y, const double *x, const int
*n);
void
vvexp2f
(float
*y, const float *x, const int
*n);
void
vvexp2
(double
*y, const double *x, const int
*n);
The exp
(), exp2
(),
and expm1
() functions conform to ISO/IEC
9899:2011.
August 16, 2012 | BSD 4 |