62 votes

Quelle est la raison mathématique pour laquelle Python a choisi d'arrondir la division des nombres entiers à l'infini négatif ?

Je connais Python // s'arrondit vers l'infini négatif et en C++ / est tronqué, arrondi vers 0.

Et voici ce que je sais jusqu'à présent :

               |remainder| 
-12 / 10  = -1,   - 2      // c++
-12 // 10 = -2,   + 8      # python

12 / -10  = -1,     2      // c++
12 // -10 = -2,   - 8      # python

12 / 10  = 1,      2       //both
12 // 10 = 1,      2 

-12 / -10 = 1,    - 2      //both
          = 2,    + 8

C++: 
1. m%(-n) == m%n
2. -m%n == -(m%n)
3. (m/n)*n + m%n == m

python:
1. m%(-n) == -8 == -(-m%n)
2. (m//n)*n + m%n == m

Mais pourquoi Python // choisir de tourner vers l'infini négatif ? Je n'ai pas trouvé de ressources expliquant cela, mais seulement trouvé et entendu des gens le dire vaguement : "pour des raisons mathématiques" .

Par exemple, dans Pourquoi -1/2 est évalué à 0 en C++, mais -1 en Python ? :

Les gens qui traitent de ces choses dans l'abstrait ont tendance à penser que qu'il est plus logique de se diriger vers l'infini négatif ( cela signifie que c'est compatible avec la fonction modulo telle qu'elle est définie en mathématiques, plutôt que de plutôt que % ayant une signification quelque peu étrange ).

Mais je ne vois pas que le C++'s / n'étant pas compatible avec la fonction modulo. En C++, (m/n)*n + m%n == m s'applique également.

Quelle est donc la raison (mathématique) pour laquelle Python choisit d'arrondir à l'infini négatif ?


Je pense que je devrais mettre ça Voici l'ancien article du blog de Guido van Rossum sur le sujet. dans le corps de la question au cas où des personnes manqueraient les commentaires.

4voto

Max Points 148

Python a // b = floor(a/b) en notation mathématique standard (ASCII). (En Allemagne, la notation de Gauss [x] est courante pour floor(x)). La fonction floor est très populaire (souvent utilisée de manière utile ; google pour voir des millions d'exemples). D'abord probablement parce qu'elle est simple et naturelle : "le plus grand nombre entier x". Par conséquent, elle bénéficie de nombreuses propriétés mathématiques intéressantes, comme :

  • Traduction par un nombre entier k : floor(x + k) = floor(x) + k.
  • Division euclidienne : x = y - q + r avec 0 r < q := floor(x/y) pour x et y donnés.

Toute définition de la fonction "arrondir vers zéro" à laquelle je peux penser serait beaucoup plus "artificielle" et impliquerait des "si-alors" (éventuellement cachés dans la valeur absolue |.| ou similaire). Je ne connais pas de tout livre de mathématiques qui introduit une fonction "arrondir vers zéro". C'est déjà une raison suffisante pour adopter cette convention plutôt que l'autre.

Je ne vais pas m'attarder sur l'argument de la "compatibilité avec l'opération modulo" détaillé dans d'autres réponses, mais il doit être mentionné car c'est bien sûr aussi un argument valable, et il est lié à la formule de "translation" ci-dessus. Par exemple dans les fonctions trigonométriques, lorsque vous avez besoin de l'angle modulo 2, c'est bien de cette division dont vous aurez besoin.

3voto

ccorn Points 208

Ici, j'écris div pour l'opérateur de division des entiers et mod pour l'opérateur opérateur de reste.

div y mod doit être défini de telle sorte que, pour a , b entiers non nuls b nous avons

a == (a div b)*b + (a mod b)

Souvent, vous voulez mod pour que les résultats soient toujours compris entre 0 (inclus) et b (exclusif), quel que soit le signe de a . Par exemple, a pourrait être un index dans un tampon circulaire, et b pourrait être sa taille (positive). Ensuite, a mod b pourrait être utilisé comme index dans le tableau de mémoire sous-jacent, même si a est négatif. En fait, en utilisant a == -1 pour faire référence au dernier élément du tampon est assez populaire.

C'est peut-être une raison pour laquelle Python arrondit les quotients vers l'infini négatif. Ainsi, le reste est soit nul, soit de même signe que le diviseur, quel que soit le signe du dividende. Voici un tracé basé sur les lettres pour un diviseur fixe :

   ^ y = x mod 5
 4 |   *    *    *    *    *
   |  *    *    *    *    *
   | *    *    *    *    *    *
   |*    *    *    *    *    *
 0 +----*----*----*----*----*->
       -5    0    5   10   15 x

En C/C++, les choses se compliquent un peu plus car les entiers ont une largeur limitée.

Supposons que a == INT_MIN qui, en représentation complémentaire à deux, est une puissance négative de deux, et b == 3 . Si nous arrondissons les quotients de telle sorte que a mod b > 0 entonces (a div b)*b devrait être inférieure à INT_MIN ce qui constituerait un dépassement de capacité d'un nombre entier signé. Les effets seraient alors définis par l'implémentation. La machine pourrait même perturber l'exécution, par exemple lors de la compilation avec la méthode de GCC -ftrapv option. Mais la raison pour laquelle on a concrétisé le comportement des opérations de division et de reste des entiers était de se débarrasser de ces incertitudes.

Par conséquent, le seul choix qui restait pour C/C++ était d'arrondir les quotients à zéro. Ainsi, le reste, s'il est différent de zéro, a le signe du dividende.

L'inconvénient est que le tracé pour un diviseur fixe semble moins régulier :

   ^ y = x mod 5
 4 |             *    *    *
   |            *    *    *
   |           *    *    *    *
   |          *    *    *    *
 0 |    *    *    *    *    *
   |   *    *
   |  *    *
   | *    *
-4 |*    *
   +----+----+----+----+----+->
       -5    0    5   10   15 x

En conséquence, mod taille de la mémoire tampon ne traite pas les valeurs d'indice négatives comme nous le souhaiterions. Du point de vue de la programmation, je n'aime pas cette décision, même si je peux comprendre le raisonnement qui consiste à remplir les conditions suivantes a == (a div b)*b + (a mod b) même dans des cas extrêmes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X