The `Math.exp()` method, as mentioned in our overview of methods
provided by the java.util.Math class,
implements the so-called **exponential function** or *e ^{x}*.
This function
crops up in a number of applications. Like a number of mathematical
functions, it is one whose output is impossible to calculate exactly

- the time required by
`Math.exp()`actually varies slightly depending on the input value; - if we require
*less*precision that theoretically allowed by a`double`, we may be able to use our own, faster, approximation at the expense of this loss of accuracy.

* Exception: as with any number raised to the power 0,
when *x* is 0, *e ^{x}* is of course equal to 1 precisely.

The implementation of `Math.exp()` relies firstly on the fact that *e ^{x}*
can be broken down as follows:

It secondly relies on the fact that because of the way floating point numbers are represented, multiplying by a power of two can be done extremely quickly (it is effectively an addition in the higher bits of the number).

The implementation thus breaks *x* down into *x _{0}* and

What all this means is that there is a "core" calculation, plus some extra
tricks that may or may not come into play depending on the range of *x*. Figure 1
shows the mean time required to calculate `Math.exp(x)` for a range of
`x` values from -1 to +2.

Figure 1: timing of `Math.exp(x)` as a function of the input value *x*.
As a guide, a floating point addition generally takes in the order of a nanosecond
on this machine.

The graph highlights firstly a range between around -0.035 and 0.035— in fact,
+/- 0.5 ln(2)— where only the "core" calculation needs to be made, taking
just under 40 nanoseconds. Then, there is
a range between around -1 and +1— in fact, +/- 1.5 ln(2)— where
*x _{0}* needs to be calcualted but where the calculation is relatively
simple (and the time rises to just under 50 nanoseconds).
Finally, for values outside this range, the calculation of

The "blip" on the graph represents the special case of 0, where we know basically
without any effort (other than the method call and test) that the result is zero.
Although not shown on this graph, a further special "fast" case also concerns cases
where the magnitude of *x* is so large that it is known in advance that the
result would overflow or underflow the possible range of values of a `double`.
This occurs when *x* exceeds around 709, and in such cases the overall time
is around 20 nanoseconds.

For some applications, it is not crucial that the most
exact value of *e*^{x}
possible be calculated: various less accurate approximations are often adequate.
A particularly useful approximation is based on the following
definition of *e ^{x}*:

Or in other words, as we pick larger and larger values of *n*,
the calculation of (1 + 1/*n*)^{n} will get closer and
closer to an accurate value of *e ^{x}*. In practice, useful
values of

double exp(x) { x = 1d + x / 256d; x *= x; x *= x; x *= x; x *= x; x *= x; x *= x; x *= x; x *= x; return x; }

is between 3 and 10 times faster than `Math.exp()` on
our i7 Q720 test system^{1}.
Of course, this approximation is less accurate than *Math.exp()*. But
it is accurate to within 3 significant figures for input values
of *x* between +/1 -10. This is almost certainly accurate enough for various applications
(for example as the basis function in a neural network implementation). If it is not
accurate enough, then we can trade in time for extra accuracy simply by increasing *n*.

1. This range has to do with the quite complex nature of Intel and indeed other modern processors. The function essentially compiles to a series of addition and multiplication instructions. Where these can be interleaved with other instructions so that each instruction is not dependent on an adjacent instruction, they can execute at up to 1 instruction per clock cycle in the best case. In the worst case, they take around 4 clock cycles per instruction.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.