This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Inconsistent division by 0 behaviour in decimal module
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.2, Python 3.3, Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: akima, facundobatista, jcea, mark.dickinson, rhettinger, skrah
Priority: normal Keywords:

Created on 2014-08-30 07:58 by akima, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (6)
msg226125 - (view) Author: Akima (akima) Date: 2014-08-30 07:58
1 / 0 (where both numbers are decimal.Decimal) produces a decimal.DivisionByZero exception as I would expect.  This is useful.  I can use a simple try except block to catch a potential division by zero error in my code.

0 / 0 (where both numbers are decimal.Decimal) produces a decimal.InvalidOperation exception.  This is undesirable.  I would expect another decimal.DivisionByZero exception.  This means that if I want to catch a division by zero error in my code using a try except block, I now have to catch exceptions for both decimal.DivisionByZero and decimal.InvalidOperation.  Presumably decimal.InvalidOperation can be raised in other scenarios, so catching it may result in masking a programming fault (which isn't just a division by zero: 0 / 0).

If you perform the same division but using standard Python integers instead of decimal.Decimal objects, the behaviour is exactly as you would expect: 0 / 0 and 1 / 0 both produce a ZeroDivisionError exception.

I have tested this in CPython 3.3.5, 3.2.3 and 2.7.3.  All versions produce the same behaviour.


Demonstration:


Python 2.7.3 (default, Feb 27 2014, 19:58:35) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from decimal import Decimal as d
>>> d(1) / d(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/decimal.py", line 1323, in __truediv__
    return context._raise_error(DivisionByZero, 'x / 0', sign)
  File "/usr/lib/python2.7/decimal.py", line 3866, in _raise_error
    raise error(explanation)
decimal.DivisionByZero: x / 0
>>> d(0) / d(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/decimal.py", line 1322, in __truediv__
    return context._raise_error(DivisionUndefined, '0 / 0')
  File "/usr/lib/python2.7/decimal.py", line 3866, in _raise_error
    raise error(explanation)
decimal.InvalidOperation: 0 / 0
>>> 1 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>> 0 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>>


Here is the same demonstration but using a Python 3.2.3 interpreter:


Python 3.2.3 (default, Feb 27 2014, 21:31:18) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from decimal import Decimal as d
>>> d(1) / d(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.2/decimal.py", line 1300, in __truediv__
    return context._raise_error(DivisionByZero, 'x / 0', sign)
  File "/usr/lib/python3.2/decimal.py", line 3926, in _raise_error
    raise error(explanation)
decimal.DivisionByZero: x / 0
>>> d(0) / d(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.2/decimal.py", line 1299, in __truediv__
    return context._raise_error(DivisionUndefined, '0 / 0')
  File "/usr/lib/python3.2/decimal.py", line 3926, in _raise_error
    raise error(explanation)
decimal.InvalidOperation: 0 / 0
>>> 1 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 0 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>>
msg226134 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2014-08-30 10:17
The behavior is according to the specification:

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


The idea behind it is that 1/0 can be reasonably defined as infinity,
whereas 0/0 is undefined.  You can see that if you disable the exceptions:

>>> c = getcontext()
>>> c.traps[DivisionByZero] = False
>>> c.traps[InvalidOperation] = False
>>> 
>>> Decimal(1) / 0
Decimal('Infinity')
>>> Decimal(0) / 0
Decimal('NaN')
>>>
msg226136 - (view) Author: Akima (akima) Date: 2014-08-30 10:57
Hi skrah.  Thanks for the feedback.  That specification is interesting.

As this IBM spec appears to be a /general/ specification for performing decimal arithmatic and not targetted specifically at Python's decimal arithmatic implementation, I would expect all of Python to adhere to its recommendations (for consitency).

If the division by 0 behaviour of the decimal module is in fact correct (as per the spec you have linked) and desirable, then perhaps the Python standard integer division by zero behaviour is incorrect.

>>> 0 / 0
... raises a ZeroDivisionError exception.  This is in conflict with the IBM spec and with the behaviour of the decimal module.  (I realize that arithmatic in the decimal module is not supposed to be equivalent to arithmatic with standard python number types, but this exception behaviour seems like something that should be consistent between the two arithmatic implementations.)
msg226137 - (view) Author: Akima (akima) Date: 2014-08-30 11:06
Sorry.  Scratch my last comment.  I see from the docs ( https://docs.python.org/3/library/decimal.html ) that the decimal module explicitly references that IBM spec.  I imagine that standard python arithmatic doesn't even attempt to conform to this ibm spec.
msg226138 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2014-08-30 11:19
According to IEEE 754-2008 binary floats should use the same exceptions
in this case.

7.2 Invalid operation
   ...
   e) division: division(0, 0) or division(∞, ∞)

7.3 Division by zero
   The divideByZero exception shall be signaled if and only if an
   exact infinite result is defined for an operation on finite
   operands.



But the Python binary float implementation is a lot older than the 2008
standard.
msg226220 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2014-09-01 08:52
> Sorry.  Scratch my last comment.  I see from the docs
>  ( https://docs.python.org/3/library/decimal.html )
>vthat the decimal module explicitly references that IBM spec. 
> I imagine that standard python arithmatic doesn't even attempt 
> to conform to this ibm spec.

That is exactly correct.

Closing as not-a-bug.
History
Date User Action Args
2022-04-11 14:58:07adminsetgithub: 66502
2014-09-01 08:52:44rhettingersetstatus: open -> closed
resolution: not a bug
messages: + msg226220
2014-08-31 15:36:45jceasetnosy: + jcea
2014-08-30 11:19:28skrahsetmessages: + msg226138
2014-08-30 11:06:41akimasetmessages: + msg226137
2014-08-30 10:57:52akimasetmessages: + msg226136
2014-08-30 10:17:32skrahsetmessages: + msg226134
2014-08-30 09:50:20pitrousetnosy: + rhettinger, facundobatista, mark.dickinson
2014-08-30 09:50:12pitrousetnosy: + skrah
2014-08-30 08:43:23akimasetcomponents: + Library (Lib)
2014-08-30 07:58:54akimacreate