L'opération modulo sur les nombres négatifs en Python

J'ai trouvé un comportement étrange en Python concernant les nombres négatifs:

>>> a = -5
>>> a % 4
3

Quelqu'un pourrait-il expliquer ce qui se passe?

47
demandé sur Noldorin 2010-10-07 19:00:39

6 réponses

Contrairement à C ou C++, l'opérateur modulo de Python (%) renvoie toujours un nombre ayant le même signe que le dénominateur (diviseur). Votre expression donne 3 parce que

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

Il est choisi par rapport au comportement C car un résultat non négatif est souvent plus utile. Un exemple est de calculer les jours de la semaine. Si aujourd'hui est mardi (jour # 2), Quel est le jour de la semaine N jours avant? En Python, nous pouvons calculer avec

return (2 - N) % 7

Mais en C, si N ≥ 3, nous obtenons un nombre négatif qui est un nombre invalide, et nous devons le réparer manuellement en ajoutant 7:

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

(Voir http://en.wikipedia.org/wiki/Modulo_operator pour comment le signe du résultat est déterminé pour différentes langues.)

81
répondu kennytm 2010-10-07 15:18:16

Voici une explication de Guido van Rossum:

Http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html

Essentiellement, c'est pour que a/b = q avec le reste r préserve les relations b*q + r = a et 0

24
répondu Kevin 2010-10-07 15:06:49

Il n'y a pas de meilleur moyen de gérer la division entière et les mods avec des nombres négatifs. Ce serait bien si a/b avait la même magnitude et le signe opposé de (-a)/b. Ce serait bien si a % b était en effet un modulo B. puisque nous voulons vraiment a == (a/b)*b + a%b, les deux premiers sont incompatibles.

Lequel garder est une question difficile, et il y a des arguments pour les deux parties. C et c++ arrondissent la division entière vers zéro (donc a/b == -((-a)/b)), et apparemment pas Python.

8
répondu David Thornley 2010-10-07 15:17:02

Comme indiqué, Python modulo fait une exception bien motivée aux conventions des autres langages.

Cela donne aux nombres négatifs un comportement transparent, en particulier lorsqu'ils sont utilisés en combinaison avec l'opérateur // integer-divide, comme % modulo l'est souvent (comme en mathématiques.divmod):

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

Produit:

 -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 % affiche toujours zéro ou positif lorsque le diviseur est positif
  • Python // tourne toujours vers le négatif infini
4
répondu Bob Stein 2018-09-03 20:44:24

Modulo, classes d'équivalence pour 4:

  • 0: 0, 4, 8, 12... et -4, -8, -12...
  • 1: 1, 5, 9, 13... et -3, -7, -11...
  • 2: 2, 6, 10... et -2, -6, -10...
  • 3: 3, 7, 11... et -1, -5, -9...

Voici un lien vers le comportement de modulo avec des nombres négatifs . (Oui, je l'ai googlé)

2
répondu wheaties 2010-10-07 15:34:29

Je pensais aussi que C'était un comportement étrange de Python. Il s'avère que je ne résolvais pas bien la division( sur papier); je donnais une valeur de 0 au quotient et une valeur de -5 au reste. Terrible... J'ai oublié la représentation géométrique des nombres entiers. En rappelant la géométrie des entiers donnés par la ligne numérique, on peut obtenir les valeurs correctes pour le quotient et le reste, et vérifier que le comportement de Python est correct. (Bien que je suppose que vous avez déjà résolu votre préoccupation, il y a longtemps).

1
répondu joser 2016-11-09 17:04:58