A operação de módulo em números negativos em Python


97

Encontrei um comportamento estranho em Python em relação aos números negativos:

>>> -5 % 4
3

Alguém poderia explicar o que está acontecendo?


27
parece certo para mim
wheaties

6
..., -9, -5, -1, 3, 7, ...
NullUserException


9
Você pode usar math.fmodpara obter o mesmo comportamento que em C ou Java.
0x2b3bfa0

Respostas:


142

Ao contrário de C ou C ++, o operador de módulo do Python ( %) sempre retorna um número com o mesmo sinal do denominador (divisor). Sua expressão produz 3 porque

(-5) / 4 = -1,25 -> piso (-1,25) = -2

(-5)% 4 = (-2 × 4 + 3)% 4 = 3.

Ele é escolhido em vez do comportamento C porque um resultado não negativo costuma ser mais útil. Um exemplo é calcular os dias da semana. Se hoje é terça-feira (dia 2), qual é o dia da semana N dias antes? Em Python podemos computar com

return (2 - N) % 7

mas em C, se N ≥ 3, obtemos um número negativo que é um número inválido e precisamos corrigi-lo manualmente adicionando 7:

int result = (2 - N) % 7;
return result < 0 ? result + 7 : result;

(Consulte http://en.wikipedia.org/wiki/Modulo_operator para saber como o sinal do resultado é determinado para diferentes idiomas.)


6
Surpreendentemente, o operador de módulo do Python (%) nem sempre retorna um número com o mesmo sinal do denominador (divisor). Consulte stackoverflow.com/questions/48347515/…
zezollo


9

Não existe uma maneira melhor de lidar com a divisão de inteiros e mods com números negativos. Seria bom se a/bfosse da mesma magnitude e sinal oposto de (-a)/b. Seria bom se a % bfosse mesmo um módulo b. Como realmente queremos a == (a/b)*b + a%b, os dois primeiros são incompatíveis.

Qual delas manter é uma questão difícil e há argumentos para ambos os lados. C e C ++ arredondam a divisão inteira para zero (portanto a/b == -((-a)/b)), e aparentemente o Python não.


1
"Seria bom se a / b tivesse a mesma magnitude e sinal oposto de (-a) / b." Por que isso seria bom? Quando esse é um comportamento desejado?
user76284

Porque ele agiria da mesma maneira que a divisão e multiplicação regulares e, portanto, é intuitivamente fácil de trabalhar. Isso pode não fazer sentido matematicamente, no entanto.
Demis

7

Como apontado, o módulo Python é uma exceção bem fundamentada às convenções de outras linguagens.

Isso dá aos números negativos um comportamento contínuo, especialmente quando usados ​​em combinação com o //operador de divisão de inteiros, como o %módulo costuma ser (como em matemática. Divmod ):

for n in range(-8,8):
    print n, n//4, n%4

Produz:

 -8 -2 0
 -7 -2 1
 -6 -2 2
 -5 -2 3

 -4 -1 0
 -3 -1 1
 -2 -1 2
 -1 -1 3

  0  0 0
  1  0 1
  2  0 2
  3  0 3

  4  1 0
  5  1 1
  6  1 2
  7  1 3
  • Python %sempre produz zero ou positivo *
  • Python //sempre gira em direção ao infinito negativo

* ... desde que o operando correto seja positivo. Por outro lado11 % -10 == -9


Obrigado, seu exemplo me fez entender :)
Lamis

6

Em python , o operador módulo funciona assim.

>>> mod = n - math.floor(n/base) * base

então o resultado é (para o seu caso):

mod = -5 - floor(-1.25) * 4
mod = -5 - (-2*4)
mod = 3

enquanto outras linguagens como C, JAVA e JavaScript usam truncamento em vez de floor.

>>> mod = n - int(n/base) * base

o que resulta em:

mod = -5 - int(-1.25) * 4
mod = -5 - (-1*4)
mod = -1

Se você precisar de mais informações sobre arredondamento em python, leia isto .



1

Também achei que era um comportamento estranho do Python. Acontece que eu não estava resolvendo bem a divisão (no papel); Eu estava atribuindo um valor de 0 ao quociente e um valor de -5 ao restante. Terrível ... Esqueci a representação geométrica dos números inteiros. Ao relembrar a geometria dos inteiros dada pela reta numérica, pode-se obter os valores corretos para o quociente e o restante e verificar se o comportamento do Python está correto. (Embora eu presuma que você já resolveu sua preocupação há muito tempo).


1

Também vale a pena mencionar que também a divisão em python é diferente de C: Considere

>>> x = -10
>>> y = 37

em C você espera o resultado

0

o que é x / y em python?

>>> print x/y
-1

e% é módulo - não o resto! Enquanto x% y em C produz

-10

python produz.

>>> print x%y
27

Você pode obter tanto como em C

A divisão:

>>> from math import trunc
>>> d = trunc(float(x)/y)
>>> print d
0

E o restante (usando a divisão acima):

>>> r = x - d*y
>>> print r
-10

Este cálculo talvez não seja o mais rápido, mas está funcionando para qualquer combinação de sinais de xey para obter os mesmos resultados que em C, além de evitar declarações condicionais.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.