Quelle est la raison de la différence entre la division entière et la conversion float en int en python?

52

J'ai récemment remarqué que int()arrondit un flotteur vers 0, tandis que la division entière arrondit un flotteur vers son plancher.

par exemple:

-7 // 2 = -4
int(-7/2) = -3

J'ai lu la documentation qui précise:

classe int (x, base = 10)

Retourne un objet entier construit à partir d'un nombre ou d'une chaîne x, ou retourne 0 si aucun argument n'est> donné. Si x est un nombre, retournez x. int (). Pour les nombres à virgule flottante, cela tronque vers zéro.

et:

division au sol

Division mathématique qui arrondit à l'entier le plus proche. L'opérateur de division de plancher est //. Par exemple, l'expression 11 // 4 est évaluée à 2 contrairement à 2,75 renvoyée par float true division. Notez que (-11) // 4 est -3 parce que c'est -2,75 arrondi vers le bas. Voir PEP 238.

Mais il me semble illogique que 2 opérations similaires (division flottante en entier) retournent des résultats différents.

Y a-t-il une motivation pour les différences entre les fonctions?

Je vous remercie.

IsaacDj
la source

Réponses:

61

Cohérence.

Vous devrez suivre des explications très basiques et apparemment non pertinentes pour le comprendre.

À l'école, vous avez appris la division avec un reste. Et vous avez fait des calculs comme celui-ci:

8 ÷ 4 = 2 R 0
7 ÷ 4 = 1 R 3
6 ÷ 4 = 1 R 2
5 ÷ 4 = 1 R 1
4 ÷ 4 = 1 R 0
3 ÷ 4 = 0 R 3
2 ÷ 4 = 0 R 2
1 ÷ 4 = 0 R 1
0 ÷ 4 = 0 R 0
        ^------ This is the result of x // 4
            ^-- This is the result of x % 4 (modulo)

Plus tard, vous avez appris les divisions pour les nombres réels:

8 ÷ 4 = 2.0
7 ÷ 4 = 1.75
6 ÷ 4 = 1.5
5 ÷ 4 = 1.25
4 ÷ 4 = 1.0
3 ÷ 4 = 0.75
2 ÷ 4 = 0.5
1 ÷ 4 = 0.25
0 ÷ 4 = 0.0
        ^--- Note that the number in front of the . is int(x/4)

Jusqu'à ce point, vous pourriez croire cela x // 4et int(x/4)toujours donner le même résultat. C'est votre compréhension actuelle de la situation.

Cependant, regardez ce qui se passe dans la division entière: le nombre derrière R passe de 3, 2, 1 à 0 puis redémarre: 3, 2, 1, 0. Le nombre devant le R décroît toutes les 4 étapes.

Alors, comment ça va se passer?

 8 ÷ 4 =  2 R 0
 7 ÷ 4 =  1 R 3
 6 ÷ 4 =  1 R 2
 5 ÷ 4 =  1 R 1
 4 ÷ 4 =  1 R 0
 3 ÷ 4 =  0 R 3
 2 ÷ 4 =  0 R 2
 1 ÷ 4 =  0 R 1
 0 ÷ 4 =  0 R 0
-1 ÷ 4 = -1 R 3
         ^------ We have to decrease now, because we already have 0 four times
              ^-- We have to restart the cycle at 3

Dans le même temps, la division du nombre réel nous donne:

-1 ÷ 4 = -0.25
          ^----- There is still a 0 in front of the .

C'est pourquoi -1 // 4donne -1 mais int(-1/4)donne 0.

Y a-t-il une motivation pour les différences entre les fonctions?

Eh bien, ils servent à des fins différentes: //fait partie d'un calcul d'entier avec des restes et int()vous donne la partie en face .d'une opération de nombre réel.

Vous décidez ce que vous voulez calculer, puis vous décidez quel opérateur utiliser en Python pour obtenir le résultat correct.

Bonne question. Continuez à apprendre.

Thomas Weller
la source
11
En pratique, cela permet une astuce: si vous avez -1 bonbons et que vous le donnez à 4 amis, il restera 3 bonbons. Super, non? Il vous suffit de découvrir comment posséder -1 bonbons.
Thomas Weller
1
Cela crée une sorte de cohérence pour autant que je comprends la motivation de l'ajout de l' //opérateur en python 3 afin d'éviter de forcer l'utilisation de int (float). Si ce n'est pas le cas, quand devrais-je choisir d'implémenter using int()et quand devrais-je implémenter using//
IsaacDj
1
Ok, alors c'est juste une mauvaise hypothèse. Ce n'est rien de mal, tant que vous testez l'exactitude de vos hypothèses, ce qui échoue probablement dans 50% des cas (du moins pour moi). J'ai ajouté quelques mots à ce sujet dans la réponse.
Thomas Weller
2
@IsaacDj vous voudrez peut-être lire ceci pour l'histoire derrière l'opérateur de "division de plancher".
bruno desthuilliers
1
@EricLippert: Je ne pense pas que ce soit bizarre. Nous ne pouvons pas supposer qu'une opération avec perte donne le même résultat que pour une opération précise. Parlé dans le code: Math.Floor(3.23) != -Math.Floor(-3.23)Pour la même raison, il -((-x)//y)n'est pas nécessaire qu'il soit égal x//y.
Thomas Weller
4

Je dirais que votre observation selon laquelle ces 2 opérations devraient être intuitivement similaires est attendue car sur les nombres positifs, elles se comportent de manière identique. Mais si vous regardez leurs origines (l'une vient des mathématiques et l'autre de l'informatique), cela rend plus logique leur comportement différent.

Vous pouvez regarder derrière ces concepts:

  • Division du sol, alias la fonction de sol appliquée à la division mathématique
  • Conversion de type / coulée de type

=================================================== ================

I) Division du sol alias la fonction de sol appliquée à la division mathématique

La fonction de plancher est un concept très bien établi en mathématiques.

De mathworld.wolfram :

La fonction de plancher | _ x_ |, également appelée la plus grande fonction entière ou valeur entière (Spanier et Oldham 1987), donne le plus grand entier inférieur ou égal à x. Le nom et le symbole de la fonction de plancher ont été inventés par KE Iverson (Graham et al. 1994)

La division du sol n'est donc rien d'autre qu'une fonction de sol appliquée à la division mathématique. Le comportement est très clair, "mathématiquement précis".

II) Conversion de type / coulée de type

De wikipedia :

En informatique, la conversion de type, la conversion de type, la coercition de type et le jonglage de type sont différentes façons de changer une expression d'un type de données à un autre.

Dans la plupart des langages de programmation, le flottant de la forme de conversion en entier est appliqué par la règle d'arrondi (il existe donc une convention):

  • Arrondir vers 0 - arrondi dirigé vers zéro (également appelé troncature)

Règle d'arrondi selon IEEE 754 .


Donc, en d'autres termes, la raison de la différence entre la division entière et la conversion float en int en python est mathématique, voici quelques réflexions de Guido van Rossum (je suppose que je n'ai pas à le présenter: D) (du blog L'histoire de Python, article "Pourquoi les étages de division entiers de Python" )

Cela dérange certaines personnes, mais il y a une bonne raison mathématique. L'opération de division entière (//) et son frère, l'opération modulo (%), vont de pair et satisfont une belle relation mathématique (toutes les variables sont des entiers):

a / b = q avec le reste r

tel que

b * q + r = a et 0 <= r <b

(en supposant que a et b sont> = 0).

kederrac
la source