9

While programming I noticed a difference between the result of math.exp(2) and math.e**2. As you can see below, this difference does not arise when calculating e^1.

Not being an experienced programmer, I wondered why this differs? I assume it has something to do with rounding up. The python docs say that math.exp(x) return e**x, but this appears not to be precisely correct. So how come that the math.exp(x) operation differs from math.e**x?

>>> math.exp(1)
2.718281828459045
>>> math.e**1
2.718281828459045
>>> math.exp(1)==math.e**1
True
>>> math.exp(2)
7.38905609893065
>>> math.e**2
7.3890560989306495
>>> math.exp(2)==math.e**2
False
>>> math.exp(100)
2.6881171418161356e+43
>>> math.e**100
2.6881171418161212e+43
>>> math.exp(100)==math.e**100
False
0x0B1
  • 330
  • 1
  • 2
  • 10
  • 2
    Related: http://stackoverflow.com/a/15322395/1639625 My guess: `math.exp` is implemented in `c` (or even in hardware) and thus has slightly different floating point behaviour. – tobias_k Jun 10 '15 at 12:59
  • 2
    Voting to reopen. There's more going on here than simply "floating-point is inaccurate". In particular, as the two answers explain, there are good reasons to expect `exp(x)` to be more accurate than `e**x`. – Mark Dickinson Jun 11 '15 at 06:27

2 Answers2

8

It's different due to differences in the implementation of the functions. Neither one is perfect due to the nature of floating point.

The ** operator is implemented in floatobject.c and contains a lot of special cases, but none of them are invoked here, so it ultimately reaches the standard C pow function. The math.pow function ultimately does the same thing.

The exp function is a straightforward wrapper around the C function of the same name, defined with a macro in mathmodule.c.

As it happens, exp is more precise (both of the results match, to within the precision allowed by floating point, high-precision answers I calculated in bc). Most likely it is because internally to pow, it is calculating the extended-precision logarithm of the double-precision e value you pass as the first argument, which is slightly smaller than 1.


The fundamental issue is that math.e, which is the number you're calculating the power of, is:

2.718281828459045 09079559829...

Whereas the real value of e is

2.718281828459045 23536028747...

And this error is compounded when you use pow or **, whereas it may not be (or may be using a higher precision value internally) if you're using exp due to the details of the algorithms it uses.

Random832
  • 37,415
  • 3
  • 44
  • 63
  • 2
    For my understanding: what is bc? – 0x0B1 Jun 10 '15 at 13:37
  • 1
    @rosie2go It's an arbitrary-precision command line calculator for unix and linux systems - since it's arbitrary precision I can set it to calculate to 50 digits and compare that result with the python float result. – Random832 Jun 10 '15 at 13:45
  • So the integer 1 becomes 0.9999999999999999468198834395860075119344401173293590545654296875 when you change it to be a float? – 0x0B1 Jun 10 '15 at 13:53
  • @rosie2go No, the point is that `e` has become a slightly smaller number, and that number's logarithm is therefore not equal to 1. And the value I listed before was a C long double, not a python float. I've edited my answer to be more clear. – Random832 Jun 10 '15 at 14:00
7

exp(x) is implemented at a much lower level than e**x. It is essentially a wrapper for a function in libc. Said function is probably (at some level) using the Taylor series expansion to calculate the value directly (or possibly some other mathematical method, I'm not sure).

On the other hand, e**x is taking a number and raising it to a power. This is an entirely different strategy, and probably less precise in most circumstances. Raising numbers to powers is difficult to do precisely.

Kevin
  • 28,963
  • 9
  • 62
  • 81