$ python --version
Python 2.7.15
$ type test.py
import random
while True:
a = random.uniform(0, 1)
b = a ** 2
c = a * a
if b != c:
print "a = {}".format(a)
print "a ** 2 = {}".format(b)
print "a * a = {}".format(c)
break
$ python test.py
a = 0.145376687586
a ** 2 = 0.0211343812936
a * a = 0.0211343812936
I was only able to reproduce this on a Windows build of Python - to be precise: Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] on win32
. On my Arch Linux box installation of Python (Python 2.7.15 (default, May 1 2018, 20:16:04) [GCC 7.3.1 20180406] on linux2
) the loop does not seem to terminate indicating that the a**2 = a * a
invariant holds there.
What is going on here? I know that IEEE floats come with a plethora of misconceptions and idiosyncrasies (this, for example, does not answer my question), but I fail to see what part of the specification or what kind of implementation of **
could possibly allow for this.
To address the duplicate flagging: This is most likely not directly an IEEE floating point math problem and more of a implementation issue of the **
operator. Therefore, this is not a duplicate of questions which are only asking about floating point issues such as precision or associativity.
Python relies on the underlying platform for its floating-point arithmetic. I hypothesize that Python’s **
operator uses a pow
implementation (as used in C) (confirmed by user2357112 referring to Python 2.7.15 source code).
Generally, pow
is implemented by using (approximations of) logarithms and exponentials, in part. This is necessary since pow
supports non-integer arguments. (Of course, this general implementation does not preclude specializations for subsets of its domain.)
Microsoft’s pow
implementation is notoriously not good. Hence, for pow(a, 2)
, it may be returning a result not equal to a*a
.