classification
Title: modulo result of Decimal differs from float/int
Type: behavior Stage: needs patch
Components: Documentation Versions: Python 3.2, Python 3.3, Python 3.4, Python 2.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: mark.dickinson Nosy List: Kotan, docs@python, mark.dickinson, python-dev, rhettinger, skrah, zacherates
Priority: normal Keywords: easy, patch

Created on 2011-05-05 08:35 by Kotan, last changed 2012-11-18 10:44 by mark.dickinson. This issue is now closed.

Files
File name Uploaded Description Edit
issue12005.diff zacherates, 2012-01-07 18:44 review
Messages (10)
msg135176 - (view) Author: Daniel Albeseder (Kotan) Date: 2011-05-05 08:35
I know that the modulo operation for negative values is not well defined, but I would at least expect that the result is the same no matter if you use ints, floats or decimals. However Decimal seem to behave else than the builtin types.

Python 3.1.2 (release31-maint, Sep 17 2010, 20:27:33) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from decimal import Decimal
>>> -3 % 5
2
>>> -3. % 5.
2.0
>>> Decimal(-3) % Decimal (5)
Decimal('-3')
>>> 

I could reproduce the same for python 2.6.6.
msg135189 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2011-05-05 11:37
I believe that this was a deliberate design decision, though now that I look it seems it's not well documented.  That should probably be fixed, so I see this as a documentation issue.

More details:

The specification on which the decimal module is based requires that there be a 'remainder' operation with the semantics of Decimal's '%' operation (i.e., the result of x % y has the sign of x).  However, the specification doesn't say anything about how the prescribed operations should map to language constructs.

So the choice was between (1) leaving '%' for Decimal objects unimplemented, and providing a separate 'remainder' method, or (2) mapping '%' to Decimal's remainder operation, and accepting the slight mismatch between the '%' semantics for float and Decimal.

Perhaps the wrong choice was made.  But it's there now, and to me it's not so obviously wrong that it's worth the upheaval of deprecating '%' for Decimal objects.

Of course there's a third choice, which is to implement the float % semantics for the Decimal type;  however, this is outside the scope of the specification---none of the specified operations matches this behaviour, and I'd oppose the addition of such an operation to the Decimal module.  There's a strong sense in which Decimal's % is a more natural operation for floating-point types than float's %.  (For one thing, it's exact in many circumstances, while float's % suffers from surprising results with negative operands.)

Reclassifying as a documentation issue.
msg135191 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2011-05-05 11:42
The doc change should also note that // and divmod suffer from a similar mismatch:

>>> Decimal(-2) // Decimal(3)
Decimal('-0')
>>> -2 // 3
-1
>>> -2.0 // 3
-1.0

However, the invariant that x = x // y * y + x % y is retained, as it should be.
msg150811 - (view) Author: Aaron Maenpaa (zacherates) Date: 2012-01-07 18:44
Here is a patch that adds an explination for the difference in the behaviour to the FAQ section of the Decimal documentation.
msg175855 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2012-11-18 10:21
New changeset 290f3b75480f by Mark Dickinson in branch '2.7':
Issue #12005: clarify behaviour of % and // for Decimal objects.
http://hg.python.org/cpython/rev/290f3b75480f
msg175856 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2012-11-18 10:23
New changeset 0ec314f26791 by Mark Dickinson in branch '3.2':
Issue #12005: clarify behaviour of % and // for Decimal objects.
http://hg.python.org/cpython/rev/0ec314f26791

New changeset f626c214cad0 by Mark Dickinson in branch '3.3':
Issue #12005: merge doc patch from 3.2
http://hg.python.org/cpython/rev/f626c214cad0

New changeset 0263d2ef9530 by Mark Dickinson in branch 'default':
Issue #12005: merge doc patch from 3.3
http://hg.python.org/cpython/rev/0263d2ef9530
msg175857 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-11-18 10:24
Docs updated.
msg175859 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2012-11-18 10:39
Mark, there's a small typo in the patch: "preseve the usual identity"
msg175860 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-11-18 10:40
D'oh!  Thanks.
msg175861 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-11-18 10:44
Typo now fixed.
History
Date User Action Args
2012-11-18 10:44:00mark.dickinsonsetmessages: + msg175861
2012-11-18 10:40:04mark.dickinsonsetmessages: + msg175860
2012-11-18 10:39:34skrahsetnosy: + skrah
messages: + msg175859
2012-11-18 10:24:05mark.dickinsonsetstatus: open -> closed
versions: + Python 3.4
messages: + msg175857

assignee: rhettinger -> mark.dickinson
resolution: fixed
2012-11-18 10:23:15python-devsetmessages: + msg175856
2012-11-18 10:21:01python-devsetnosy: + python-dev
messages: + msg175855
2012-01-07 18:44:10zacheratessetfiles: + issue12005.diff

nosy: + zacherates
messages: + msg150811

keywords: + patch
2011-12-02 16:31:07ezio.melottisetkeywords: + easy
stage: needs patch
versions: + Python 2.7, Python 3.2, Python 3.3, - Python 2.6, Python 3.1
2011-05-05 13:53:01rhettingersetassignee: docs@python -> rhettinger

nosy: + rhettinger
2011-05-05 11:42:53mark.dickinsonsetmessages: + msg135191
2011-05-05 11:37:36mark.dickinsonsetnosy: + docs@python
messages: + msg135189

assignee: docs@python
components: + Documentation, - Library (Lib)
2011-05-05 09:31:55ezio.melottisetnosy: + mark.dickinson
2011-05-05 08:35:18Kotancreate