classification
Title: Decimal(0)**0 is an error, 0**0 is 1, but Decimal(0) == 0
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.4, Python 3.5, Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: Devin Jeanpierre, cvrebert, ezio.melotti, facundobatista, josh.r, mark.dickinson, rhettinger, skrah, steven.daprano, terry.reedy, tim.peters, vstinner
Priority: low Keywords:

Created on 2015-01-09 03:13 by Devin Jeanpierre, last changed 2015-05-15 00:12 by rhettinger. This issue is now closed.

Messages (16)
msg233711 - (view) Author: Devin Jeanpierre (Devin Jeanpierre) * Date: 2015-01-09 03:13
>>> import decimal
>>> x = 0
>>> y = float(x)
>>> z = decimal.Decimal(x)
>>> x == y == z == x
True
>>> x ** x
1
>>> y**y
1.0
>>> z**z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/decimal.py", line 2216, in __pow__
    return context._raise_error(InvalidOperation, '0 ** 0')
  File "/usr/lib/python2.7/decimal.py", line 3872, in _raise_error
    raise error(explanation)
decimal.InvalidOperation: 0 ** 0

This is PHP-like and confusing, and maybe not justified just by standards compliance. If it is justified by standards compliance, this deserves to be spelled out in big red letters in the documentation for the decimal module, along with any other inconsistencies.
msg233712 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2015-01-09 03:27
In the code there is this comment:
    # 0**0 = NaN (!), x**0 = 1 for nonzero x (including +/-Infinity)
and raising the error for this specific case seems intentional.
msg233713 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2015-01-09 03:33
Intentional, but really hard to justify from a consistency perspective. There appear to be several reasonable arguments to treat it as 1 regardless of the mathematical impurity ( https://www.math.hmc.edu/funfacts/ffiles/10005.3-5.shtml ), and since we clearly accept that for int and float, it seems reasonable to extend it to Decimal.
msg233714 - (view) Author: Devin Jeanpierre (Devin Jeanpierre) * Date: 2015-01-09 03:37
Yes, also, it is documented: https://docs.python.org/3/library/decimal.html#decimal.InvalidOperation

Still, the status quo is bad. At the very least there should be clear documentation on how Decimal differs in behavior from floats and ints. (Other than the obvious, like 1/5 taking on a different value -- although explicitly mentioning that in the list might be a good idea.)

BTW, 0**0=1 is not mathematically impure. It at one point was fairly well accepted as the right answer, since it's the one that tends to come out naturally . e.g. http://arxiv.org/abs/math/9205211 page 6 ("ripples") . This might explain why ints and floats so casually evaluate 0**0 to 1.
msg233715 - (view) Author: Chris Rebert (cvrebert) * Date: 2015-01-09 03:57
This behavior seems to be required by the General Decimal Arithmetic
Specification (http://speleotrove.com/decimal/daexcep.html ):

> The following exceptional conditions can occur:
>     [...]
>     Invalid operation
>         This occurs and signals "invalid-operation" if:
>             [...]
>             * both operands of the "power" operation are zero

"signals invalid-operation" apparently being mapped by default in Python to "raise the InvalidOperation exception".
msg233728 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2015-01-09 08:18
> this deserves to be spelled out in big red letters in 
> the documentation for the decimal module, along with 
> any other inconsistencies.

I think you lost all sense of proportion here.  The decimal module is obliged to follow the decimal spec (that is its reason for existence).

The decimal module docs are already create a heavy mental load and their usability would not be improved shifting focus to corner case inconsistencies between types that haven't proven to be an issue in practice.  If you were to go write a blog post about 0**0 versus Decimal(0)**0, I think you would find that no one cares.
msg233729 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-01-09 08:18
> This behavior seems to be required by the General Decimal Arithmetic
Specification (http://speleotrove.com/decimal/daexcep.html )

Yes, exactly.  The decimal module strictly follows that specification.  I don't like the 0**0 -> NaN result much either (especially when we also have inf**0 -> 1), but it's what's specified.  I've talked to Mike Cowlishaw (the author of the specification) about this particular issue, and the spec is not likely to change on this point.
msg233732 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-01-09 08:39
> the spec is not likely to change on this point.

In this case, we should just document the behaviour with a reference to the General Decimal Arithmetic Specification.
msg233735 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2015-01-09 08:45
The docs already reference the spec.
msg233740 - (view) Author: Devin Jeanpierre (Devin Jeanpierre) * Date: 2015-01-09 09:23
Does the spec have a handy list of differences to floats anywhere, or do you have to internalize the whole thing?
msg233749 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2015-01-09 12:53
The behavior is already documented (power function):

  "at least one of x or y must be nonzero"


The decimal docs are already so large that it is probably a bad
idea to add a warning.
msg233796 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2015-01-10 02:46
Mark Dickson wrote:
> I've talked to Mike Cowlishaw (the author of the specification)
> about this particular issue, and the spec is not likely to 
> change on this point.

I'm curious about the rationale for the decision. As I'm sure you're aware, in general 0**0 is treated as 1 by both a majority (I think) of mathematicians and programming languages. As Knuth puts it, the binomial theorem is too important to do otherwise. IEEE 754 treats it as 1, although the 2008 revision adds a second power function powr() which returns NAN if both arguments are 0. So I wonder why the decimal spec choose to do otherwise?

(Not saying they're wrong to do so.)
msg233798 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015-01-10 03:08
This is easy:  Cowlishaw is wrong on this one, but nothing can be done about it ;-)

Confusion arises because most people think of 0**0 as a value (where it certainly must be 1) while others seem to view it as some kind of shorthand for expressing a limit (as the base and/or exponent _approach_ 0, in which case there is no correct answer - it's an "indeterminate form").

It's in the "spirit of 754" to take inputs at face value, viewing them as infinitely precise.  So viewing 0**0 as anything other than 1 in this context is perverse.

Centuries of history distilled to a few paragraphs here:

http://en.wikipedia.org/wiki/Exponentiation#Zero_to_the_power_of_zero
msg233808 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2015-01-10 09:16
Thank you Tim.

A possibility would be to add "(disallowed by the Decimal standard)" to the exception message.
msg233809 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2015-01-10 09:27
> A possibility would be to add "(disallowed by the 
> Decimal standard)" to the exception message.

That is what InvalidOperation means ;-)
msg233820 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2015-01-10 12:28
For the differences between the standard and IEEE 754-2008 we could
link to:

  http://speleotrove.com/decimal/dascope.html


In the long run, perhaps we should move to IEEE, because we're
almost there (but that's a separate issue).
History
Date User Action Args
2015-05-15 00:12:19rhettingersetstatus: open -> closed
resolution: not a bug
2015-01-10 12:28:46skrahsetmessages: + msg233820
2015-01-10 09:27:56rhettingersetmessages: + msg233809
2015-01-10 09:16:44terry.reedysetnosy: + terry.reedy

messages: + msg233808
versions: - Python 3.6
2015-01-10 03:08:17tim.peterssetnosy: + tim.peters
messages: + msg233798
2015-01-10 02:46:30steven.dapranosetnosy: + steven.daprano
messages: + msg233796
2015-01-09 12:53:29skrahsetmessages: + msg233749
2015-01-09 09:23:22Devin Jeanpierresetmessages: + msg233740
2015-01-09 08:45:49rhettingersetmessages: + msg233735
2015-01-09 08:39:42vstinnersetnosy: + vstinner
messages: + msg233732
2015-01-09 08:18:45mark.dickinsonsetmessages: + msg233729
2015-01-09 08:18:35rhettingersetpriority: normal -> low
assignee: rhettinger
messages: + msg233728
2015-01-09 03:57:15cvrebertsetnosy: + cvrebert
messages: + msg233715
2015-01-09 03:37:43Devin Jeanpierresetmessages: + msg233714
2015-01-09 03:33:29josh.rsetnosy: + josh.r
messages: + msg233713
2015-01-09 03:27:14ezio.melottisetnosy: + facundobatista, ezio.melotti, rhettinger, skrah, mark.dickinson

messages: + msg233712
versions: - Python 3.2, Python 3.3
2015-01-09 03:13:11Devin Jeanpierresettype: behavior
2015-01-09 03:13:06Devin Jeanpierrecreate