0

I was trying to understand the math behind calculations using / and // and % operators by doing some trials and found the results are similar to calculator only when using Decimal() but without it the results kinda confusing, i tried to add comments #No Ideato my code to mark the points i don't understand,for example:

  1. in this trial for % operator by applying signed and unsigned number the results and with and without Decimal() the results are :

    >>> 9%5    #This result will be the reminder
    4         
    >>> (-9)%5    #No Idea
    1
    >>> Decimal(9)% Decimal(5)    #This result will be the reminder
    Decimal('4')  
    >>> Decimal(-9)% Decimal(5)    #The result will be the signed reminder
    Decimal('-4')
    
  2. in this trial for // operator and using signed and unsigned number with and without Decimal() the results are :

    >>> 9//5    #int result
    1
    >>> -9//5    #No Idea
    -2
    >>> Decimal(9)/Decimal(5)    #Same result as using calculator
    Decimal('1.8')
    >>> Decimal(-9)//Decimal(5)    #No Idea
    Decimal('-1')
    

Please consider that this question is not a duplicate and i have done some research to get an answer but i found some answered questions that explain only about // operator using only positive signed numbers and doesn't include information about negative signed numbers or using the Decimal() and doesn't have answer about % operator.

so,It will be helpful if someone knows why the results are different and how they are calculated.

WAlbaker
  • 13
  • 1
  • 6
  • Possible duplicate of [In Python 2, what is the difference between '/' and '//' when used for division?](https://stackoverflow.com/questions/183853/in-python-2-what-is-the-difference-between-and-when-used-for-division) – metatoaster Oct 13 '18 at 07:50
  • 3
    See the beginning of the [Binary arithmetic operations](https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations) section of the documentation, especially the part about the sign of result that the `%` (modulo) operator yields. When you're doing research, the documentation is often a good place to look—at least with Python. – martineau Oct 13 '18 at 08:18
  • I don't think thi duplication is correct, as this question handles the difference between the built-in integer and the `Decimal` class primarily. – JohanL Oct 13 '18 at 08:59

2 Answers2

2

Explanation for the behaviour of integers

From python documentation:

Division of integers yields a float, while floor division of integers results in an integer; the result is that of mathematical division with the ‘floor’ function applied to the result.

Therefore, an integer division (//) of negative negative and positive number works as follows:

-9 // 5 == floor(-9 / 5) == floor(-1.8) == -2

The modulo operator is the remainder of the integer division, i.e. x % y = x - x // y * y. In your example:

-9 % 5 == -9 - (-9 // 5 * 5) == (-9) - (-2 * 5) == (-9) - (-10) == 1

The documentation also says:

The modulo operator always yields a result with the same sign as its second operand (or zero); the absolute value of the result is strictly smaller than the absolute value of the second operand.

But that comes naturally from the formula above, e.g.:

9 % -5 == 9 - (9 // (-5) * (-5)) == 9 - (-2 * (-5)) == 9 - 10 == -1

decimal.Decimal is different

The documentation explains the difference well:

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')

The integer division operator // behaves analogously, returning the integer part of the true quotient (truncating towards zero) rather than its floor, so as to preserve the usual identity x == (x // y) * y + x % y:

>>> -7 // 4
-2
>>> Decimal(-7) // Decimal(4)
Decimal('-1')
zvone
  • 18,045
  • 3
  • 49
  • 77
0

As I understand the question, the OP is asking about the different behavior between Python integers and Decimals. I don't think there is any good reason for it. Both choices are possible, but it is a bit confusing for the user that they differ.

Let's call the numerator n, the denominator d and split the result in the interger result i and the remainder r. This means that

n // d = i
n % d = r

For the operations to make sense, we need

i * d + r == n

For n = -9 and d = 5 we see that this is uphold for both i = -1, r = -4 and for i = -2, r = 1 as can be seen by

(i = -1, r = -4) => -1 * 5 + -4 == -9
(i = -2, r = 1) => -2 * 5 + 1 == -9

Now, in Python integer division is defined as always truncate towards minus infinity (down) and the Decimal implementation has chosen to round towards zero. That means that positive values are truncated/rounded down, whereas negative values are rounded up.

Rounding towards zero is the choice made also made in the C language. However, my personal opinion is that the Python choice is much more sane, specifically coming from a hardware background. And given that this is the choice made in Python, I think it is strange (and bad) that Decimal has chosen to do as in the C language.

JohanL
  • 6,671
  • 1
  • 12
  • 26
  • "I don't think there is any good reason for it." Not sure about "good", but there's definitely a reason. The `decimal` module follows a [standard](http://speleotrove.com/decimal/). That standard specifies a [remainder](http://speleotrove.com/decimal/daops.html#refremain) operation where the sign of the result matches that of the dividend (unlike the behaviour of % with integers, where the sign of the result matches that of the divisor). So it's somewhat natural to have `%` for the `Decimal` type correspond to that remainder operation. – Mark Dickinson Oct 13 '18 at 09:48
  • The standard also specifies [divide-integer](http://speleotrove.com/decimal/daops.html#refdivint), with semantics matching those of `//` for `Decimal` objects. – Mark Dickinson Oct 13 '18 at 09:51
  • @MarkDickinson Yes, it follows _a_ standard. But it is strange choise of standard when you get different results for the standard arithmetic operations. – JohanL Oct 13 '18 at 10:40
  • Shrug. It's not as though there was a huge variety of standards to choose from back in 2003 when the `decimal` module was planned, and it's definitely better to follow a standard than to just make up all the corner cases. So maybe it wasn't the perfect standard; it was just better than the few other options. :-) – Mark Dickinson Oct 13 '18 at 11:59
  • @MarkDickinson Well, there was also the option to follow the behavior of Python integers, which would lead to fewer surprises. That I personally happen to prefer the way it is in Python, would just be a bonus. :-) – JohanL Oct 13 '18 at 16:19