web-dev-qa-db-fra.com

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

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

>>> -5 % 4
3

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

59
facha

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 sur le 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? Dans 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 besoin de 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 savoir comment le signe du résultat est déterminé pour différentes langues.)

95
kennytm

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 <= r <b.

25
Kevin

Il n'y a pas de meilleure façon 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 ampleur et le signe opposé de (-a)/b. Ce serait bien si a % b Était bien 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 côtés. C et C++ divisent l'entier par division vers zéro (donc a/b == -((-a)/b)), et apparemment Python ne le fait pas.

8
David Thornley

Comme indiqué, Python modulo fait une exception bien raisonné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 le // opérateur de division d'entier, comme % modulo 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 % sort toujours zéro ou positif lorsque le diviseur est positif
  • Python // arrondit toujours vers l'infini négatif
4
Bob Stein

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 comportement de modulo avec des nombres négatifs . (Oui, je l'ai googlé)

3
wheaties

J'ai aussi pensé 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ée par la droite 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 problème il y a longtemps).

1
joser

Il convient également de mentionner que la division dans python est différente de C: Considérez

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

en C vous attendez le résultat

0

qu'est-ce que x/y en python?

>>> print x/y
-1

et% est modulo - pas le reste! Alors que x% y en C donne

-10

rendement en python.

>>> print x%y
27

Vous pouvez obtenir les deux comme en C

La division:

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

Et le reste (en utilisant la division ci-dessus):

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

Ce calcul n'est peut-être pas le plus rapide, mais il fonctionne pour toutes les combinaisons de signes de x et y pour obtenir les mêmes résultats qu'en C plus il évite les instructions conditionnelles.

0
bebbo