Was ist der Grund für den Unterschied zwischen der Ganzzahldivision und der Konvertierung von Float zu Int in Python?

52

Ich habe kürzlich bemerkt, dass int()ein Float in Richtung 0 gerundet wird, während eine Ganzzahldivision einen Float in Richtung seines Bodens rundet.

zum Beispiel:

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

Ich habe die Dokumentation gelesen, in der Folgendes angegeben ist:

Klasse int (x, Basis = 10)

Gibt ein ganzzahliges Objekt zurück, das aus einer Zahl oder Zeichenfolge x aufgebaut ist, oder gibt 0 zurück, wenn keine Argumente angegeben sind. Wenn x eine Zahl ist, geben Sie x zurück. int (). Bei Gleitkommazahlen wird dies gegen Null abgeschnitten.

und:

Bodenteilung

Mathematische Division, die auf die nächste ganze Zahl abgerundet wird. Der Floor Division Operator ist //. Zum Beispiel ergibt der Ausdruck 11 // 4 2 im Gegensatz zu 2,75, die von float true Division zurückgegeben werden. Beachten Sie, dass (-11) // 4 -3 ist, da dies -2,75 nach unten gerundet ist. Siehe PEP 238.

Für mich erscheint es jedoch unlogisch, dass zwei ähnliche Operationen (Float-Division zu Integer) unterschiedliche Ergebnisse liefern sollten.

Gibt es eine Motivation für die Unterschiede zwischen den Funktionen?

Vielen Dank.

IsaacDj
quelle

Antworten:

61

Konsistenz.

Sie müssen einige sehr grundlegende und scheinbar irrelevante Erklärungen befolgen, um es zu verstehen.

In der Schule haben Sie mit einem Rest Teilung gelernt. Und Sie haben folgende Berechnungen durchgeführt:

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)

Später haben Sie Divisionen für reelle Zahlen gelernt:

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)

Bis zu diesem Punkt könnten Sie das glauben x // 4und int(x/4)immer das gleiche Ergebnis erzielen. Das ist Ihr aktuelles Verständnis der Situation.

Schauen Sie sich jedoch an, was in der Ganzzahldivision passiert: Die Zahl hinter R wechselt von 3, 2, 1 bis 0 und startet dann neu: 3, 2, 1, 0. Die Zahl vor dem R wird bei jedem vierten Schritt verringert.

Also, wie wird es weitergehen?

 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

Gleichzeitig gibt uns die reelle Zahlenteilung:

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

Deshalb -1 // 4gibt -1, aber int(-1/4)0.

Gibt es eine Motivation für die Unterschiede zwischen den Funktionen?

Nun, sie dienen verschiedenen Zwecken: Sie //sind Teil einer Ganzzahlberechnung mit Resten und int()geben Ihnen den Teil vor .einer reellen Zahlenoperation.

Sie entscheiden, was Sie berechnen möchten, und dann, welchen Operator Sie in Python verwenden möchten, um das richtige Ergebnis zu erhalten.

Gute Frage. Lerne weiter.

Thomas Weller
quelle
11
In der Praxis erlaubt dies einen Trick: Wenn Sie -1 Süßigkeiten haben und diese an 4 Freunde verschenken, bleiben 3 Süßigkeiten übrig. Großartig, nicht wahr? Sie müssen nur herausfinden, wie Sie -1 Süßigkeiten besitzen.
Thomas Weller
1
Es schafft eine gewisse Konsistenz, aber soweit ich verstehe, besteht die Motivation beim Hinzufügen des //Operators in Python 3 darin, die Verwendung von int (float) nicht zu erzwingen. Wenn dies nicht der Fall ist, wann sollte ich mich für die Implementierung mit entscheiden int()und wann sollte ich mit implementieren//
IsaacDj
1
Ok, dann ist es nur eine falsche Annahme. Das ist nichts Schlimmes, solange Sie Ihre Annahmen auf Richtigkeit prüfen, was wahrscheinlich in 50% der Fälle fehlschlägt (zumindest für mich). Ich habe in der Antwort einige Worte dazu hinzugefügt.
Thomas Weller
2
@IsaacDj Vielleicht möchten Sie dies für die Geschichte hinter dem Operator "Floor Division" lesen .
Bruno Desthuilliers
1
@ EricLippert: Ich finde es nicht bizarr. Wir können nicht davon ausgehen, dass eine verlustbehaftete Operation das gleiche Ergebnis liefert wie eine präzise Operation. Im Code gesprochen: Math.Floor(3.23) != -Math.Floor(-3.23)Aus dem gleichen Grund -((-x)//y)muss nicht gleich sein x//y.
Thomas Weller
4

Ich würde sagen, dass Ihre Beobachtung, dass diese beiden Operationen intuitiv ähnlich sein sollten, erwartet wird, da sie sich bei positiven Zahlen identisch verhalten. Aber wenn man sich ihre Ursprünge ansieht (einer stammt aus der Mathematik und der andere aus der Informatik), dann macht es mehr Sinn, dass sie sich unterschiedlich verhalten.

Sie können hinter diese Konzepte schauen:

  • Bodenteilung, auch bekannt als Bodenfunktion, die auf die mathematische Division angewendet wird
  • Typumwandlung / Typguss

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

I) Bodenteilung, auch bekannt als die Bodenfunktion, die auf die mathematische Division angewendet wird

Die Bodenfunktion ist ein sehr gut etabliertes Konzept in der Mathematik.

Von mathworld.wolfram :

Die Bodenfunktion | _ x_ |, auch als größte Ganzzahlfunktion oder ganzzahliger Wert bezeichnet (Spanier und Oldham 1987), ergibt die größte Ganzzahl kleiner oder gleich x. Der Name und das Symbol für die Bodenfunktion wurden von KE Iverson geprägt (Graham et al. 1994)

Die Bodenteilung ist also nichts anderes als eine Bodenfunktion, die auf die mathematische Division angewendet wird. Das Verhalten ist sehr klar, "mathematisch präzise".

II) Typumwandlung / Typguss

Aus Wikipedia :

In der Informatik sind Typkonvertierung, Typumwandlung, Typenzwang und Typjonglierung verschiedene Möglichkeiten, einen Ausdruck von einem Datentyp in einen anderen zu ändern.

In den meisten Programmiersprachen wird die Casting-Form float to integer durch eine Rundungsregel angewendet (es gibt also eine Konvention):

  • Rundung gegen 0 - gerichtete Rundung gegen Null (auch als Kürzung bezeichnet)

Rundungsregel nach IEEE 754 .


Mit anderen Worten, der Grund für den Unterschied zwischen der Ganzzahldivision und der Umwandlung von Float in Int in Python ist ein mathematischer. Hier einige Gedanken von Guido van Rossum (ich glaube, ich muss ihn nicht vorstellen: D) (aus dem Blog Die Geschichte von Python, Artikel "Warum Pythons Integer Division Floors" )

Dies stört einige Leute, aber es gibt einen guten mathematischen Grund. Die Ganzzahldivisionsoperation (//) und ihr Geschwister, die Modulooperation (%), passen zusammen und erfüllen eine schöne mathematische Beziehung (alle Variablen sind Ganzzahlen):

a / b = q mit Rest r

so dass

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

(unter der Annahme, dass a und b> = 0 sind).

Kederrac
quelle