Mais pourquoi Python //
choisir d'arrondir vers l'infini négatif ?
Je ne suis pas sûr que le site La raison pour laquelle ce choix a été fait à l'origine n'est documentée nulle part (bien que, pour ce que j'en sais, elle pourrait être expliquée en détail dans un PEP quelque part), mais nous pouvons certainement trouver diverses raisons pour lesquelles cela a du sens.
L'une des raisons est simplement que l'arrondi vers l'infini négatif (ou positif !) signifie que tous les nombres sont arrondis de la même manière, alors que l'arrondi vers zéro rend le zéro spécial. La façon mathématique de le dire est que l'arrondi vers le bas vers est invariant de translation c'est-à-dire qu'il satisfait à l'équation :
round_down(x + k) == round_down(x) + k
pour tous les nombres réels x
et tous les entiers k
. L'arrondi vers zéro ne le fait pas, puisque, par exemple :
round_to_zero(0.5 - 1) != round_to_zero(0.5) - 1
Bien sûr, d'autres arguments existent aussi, comme celui que vous citez et qui se base sur la compatibilité avec (comment nous voudrions) les %
opérateur (pour se comporter) - plus d'informations à ce sujet ci-dessous.
En effet, je dirais que le réel La question qui se pose ici est de savoir pourquoi le programme Python int()
La fonction est no défini pour arrondir les arguments à virgule flottante vers l'infini négatif, de sorte que m // n
serait égal à int(m / n)
. (Je soupçonne des "raisons historiques".) Encore une fois, ce n'est pas si grave, puisque Python dispose au moins de math.floor()
qui satisfait m // n == math.floor(m / n)
.
Mais je ne vois pas le C++ 's /
n'étant pas compatible avec la fonction modulo. En C++, (m/n)*n + m%n == m
s'applique également.
C'est vrai, mais garder cette identité tout en ayant /
Pour arrondir vers zéro, il faut définir %
de manière maladroite pour les nombres négatifs. En particulier, nous perdons les deux propriétés mathématiques utiles suivantes de Python %
:
-
0 <= m % n < n
pour tous m
et tous les positifs n
; et
-
(m + k * n) % n == m % n
pour tous les entiers m
, n
y k
.
Ces propriétés sont utiles car l'une des principales utilisations de la %
c'est "envelopper" un nombre m
à une gamme limitée de longueur n
.
Par exemple, disons que nous essayons de calculer des directions : disons que heading
est notre actuel cap compas en degrés (comptés dans le sens des aiguilles d'une montre à partir du plein nord, avec 0 <= heading < 360
) et que nous voulons calculer notre nouveau cap après avoir tourné angle
degrés (où angle > 0
si on tourne dans le sens des aiguilles d'une montre, ou angle < 0
si on tourne dans le sens inverse des aiguilles d'une montre). En utilisant l'outil Python %
nous pouvons calculer notre nouvelle rubrique simplement comme suit :
heading = (heading + angle) % 360
et cela fonctionnera simplement dans tous les cas.
Cependant, si nous essayons d'utiliser cette formule en C++, avec ses différentes règles d'arrondi et ses différences correspondantes, nous ne pourrons pas utiliser cette formule. %
opérateur, nous constaterons que l'enveloppement ne fonctionne pas toujours comme prévu ! Par exemple, si nous commençons par faire face au nord-ouest ( heading = 315
) et tourner de 90° dans le sens des aiguilles d'une montre ( angle = 90
), nous allons en effet nous retrouver face au nord-est ( heading = 45
). Mais si alors essayer de faire demi-tour 90° dans le sens inverse des aiguilles d'une montre ( angle = -90
), avec le C++ %
opérateur, nous ne finirons pas par revenir à heading = 315
comme prévu, mais plutôt à heading = -45
!
Pour obtenir un comportement enveloppant correct en utilisant le langage C++. %
nous devrons plutôt écrire la formule comme suit :
heading = (heading + angle) % 360;
if (heading < 0) heading += 360;
ou comme :
heading = ((heading + angle) % 360) + 360) % 360;
(La formule plus simple heading = (heading + angle + 360) % 360
ne fonctionnera que si nous pouvons toujours garantir que heading + angle >= -360
.)
C'est le prix à payer pour avoir une règle d'arrondi non invariante en translation pour la division et, par conséquent, une règle d'arrondi non invariante en translation pour la division. %
opérateur.