Modulo operation on a python negative decimal.Decimal and a positive int

After a long search (because searching on "%", "mod", "modulo" etc. gives a thousand of results), I finally found that, surprisingly, this is intended:

There are some small differences between arithmetic on Decimal objects and arithmetic on integers and floats. When the remainder operator % is applied to Decimal objects, the sign of the result is the sign of the dividend rather than the sign of the divisor:

>>> (-7) % 4
1
>>> Decimal(-7) % Decimal(4)
Decimal('-3')

I don't know the reason for this, but it looks like it's not possible to change this behaviour (without patching).


Python behaves according to IBM's General Decimal Arithmetic Specification.

The remainder is defined as:

remainder takes two operands; it returns the remainder from integer division. […]

the result is the residue of the dividend after the operation of calculating integer division as described for divide-integer, rounded to precision digits if necessary. The sign of the result, if non-zero, is the same as that of the original dividend.

So because Decimal('-45') // D('360') is Decimal('-0'), the remainder can only be Decimal('-45').

Though why is the quotient 0 and not -1? The specification says:

divide-integer takes two operands; it divides two numbers and returns the integer part of the result. […]

the result returned is defined to be that which would result from repeatedly subtracting the divisor from the dividend while the dividend is larger than or equal to the divisor. During this subtraction, the absolute values of both the dividend and the divisor are used: the sign of the final result is the same as that which would result if normal division were used. […]

Notes: […]

  1. The divide-integer and remainder operations are defined so that they may be calculated as a by-product of the standard division operation (described above). The division process is ended as soon as the integer result is available; the residue of the dividend is the remainder.

How many times can you subtract 360 from 45? 0 times. Is an integer result available? It is. Then the quotient is 0 with a minus sign because the divide operation says that

The sign of the result is the exclusive or of the signs of the operands.

As for why the Decimal Specification goes on this route, instead of doing it like in math where the remainder is always positive, I'm speculating that it could be for the simplicity of the subtraction algorithm. No need to check the sign of the operands in order to compute the absolute value of the quotient. Modern implementations probably use more complicated algorithms anyway, but simplicity could be have an important factor back in the days when the standard was taking form and hardware was simpler (way fewer transistors). Fun fact: Intel switched from radix-2 integer division to radix-16 only in 2007 with the release of Penryn.