Search code examples
pythonpython-2.xarithmetic-expressions

Ambiguity in basic arithmetic operations Python


Here is the scenario:

In [5]: (2.0 - 5.0**(0.5)) ** (1.0/3.0)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-5-d064023f1ac5> in <module>()
----> 1 (2.0 - 5.0**(0.5)) ** (1.0/3.0)

ValueError: negative number cannot be raised to a fractional power

In [7]: -1.0 ** (1.0/3.0)
Out[7]: -1.0

The above operation is being done on a python interpreter. For the first expression, it is giving value error and says that negative number can't have fractional power !! So, firstly, why is this error as obviously, -ve numbers can have cube root or fifth root etc. Also, if that is the case, it should be consistent while in second case, it gives no error when -1 is raised to fractional power (1/3).

Can someone explain why is this the case?


Solution

  • The ** operator has specific binding behaviour; from the power operator documentation:

    The power operator binds more tightly than unary operators on its left; it binds less tightly than unary operators on its right.

    [...]

    Thus, in an unparenthesized sequence of power and unary operators, the operators are evaluated from right to left (this does not constrain the evaluation order for the operands): -1**2 results in -1.

    So your second example is executed as:

    -(1.0 ** (1.0/3.0))
    

    That is to say, the - unary operator applies to the result of ** as that operator binds more tightly. As a result you have positive number is raised to the power 1/3rd, and only then made negative.

    In your first example, the expression is parsed as

    (2.0 - (5.0**(0.5))) ** (1.0/3.0)
    

    There is no unary operator here, but the ** power operator does have a higher precedence than the binary - subtraction operator.

    This then resolves as

    (2.0 - 2.23606797749979) ** (1.0/3.0)
    

    which is

    (-0.2360679774997898) ** (1.0/3.0)
    

    so is trying to raise a negative number to a fraction.

    Python 2 ** (and the pow() function) don't support producing a complex number support when the inputs are at most float objects. Convert your negative float value to a complex() number first:

    >>> (-1.0) ** 0.5
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: negative number cannot be raised to a fractional power
    >>> (-1+0j) ** 0.5
    (6.123233995736766e-17+1j)
    >>> (2+0j - 5.0**(0.5)) ** (1.0/3.0)
    (0.30901699437494756+0.535233134659635j)
    

    This changed in Python 3, where a complex result is returned for a negative number raised to a fractional power.