Serhiy's objection is a little subtler than that. The Python expression `math.log(math.e)` in fact yields exactly 1.0, so IF it were the case that x**y were implemented as
math.exp(math.log(x) * y)
THEN math.e**500 would be computed as math.exp(math.log(math.e) * 500) == math.exp(1.0 * 500) == math.exp(500.0).
But that's not how x**y is implemented. Because the error in log() is multiplied by y, and then fed into exp() blowing it up even more, only a hopelessly naive library would implement pow() that way. In practice, library pow functions fake the effect of at least 15 more bits than native double precision to absorb these errors.
Under the covers, then, a reasonable library pow computes math.log(math.e) to more than native double precision - and _that_ (internal, invisible) result is not 1.0. Because math.e isn't the mathematical e to begin with. The difference between math.e and the mathematical e is a difference quite visible to the internal log, which delivers an internal log not equal to 1, and its difference from 1 is "an error" multiplied by 500 and fed into the internal exp (blowing up the error even more).
In the end, math.e**500 returns a very good approximation to the true value, _given_ that math.e is not e. There's no reason to hope that's close to exp(500), though - that delivers a very good approximation to e**500 (where `e` is the true e). The larger the exponent, the more different math.e**y _should be_ from exp(y), and for the same fundamental reason 2**y differs from 3**y (or plug in any other pair of distinct bases).
All that said, I agree with Mark that math.e is at best an attractive nuisance. Still, I'm -1 on removing it - it's a traditional and universally embraced nuisance ;-) |