Die Modulo-Operation für negative Zahlen in Python

93

Ich habe in Python ein seltsames Verhalten in Bezug auf negative Zahlen festgestellt:

>>> -5 % 4
3

Könnte jemand erklären, was los ist?

facha
quelle
25
sieht für mich
richtig aus
6
..., -9, -5, -1, 3, 7, ...
NullUserException
2
mögliches Duplikat von C, Python - unterschiedliches Verhalten der Modulo (%)
-Operation
7
Sie können math.fmoddas gleiche Verhalten wie in C oder Java verwenden.
0x2b3bfa0

Antworten:

134

Im Gegensatz zu C oder C ++ gibt Pythons Modulo-Operator ( %) immer eine Zahl mit dem gleichen Vorzeichen wie der Nenner (Divisor) zurück. Ihr Ausdruck ergibt 3, weil

(-5) / 4 = -1,25 -> Boden (-1,25) = -2

(-5)% 4 = (-2 × 4 + 3)% 4 = 3.

Es wird dem C-Verhalten vorgezogen, da ein nicht negatives Ergebnis oft nützlicher ist. Ein Beispiel ist die Berechnung von Wochentagen. Wenn heute Dienstag (Tag 2) ist, was ist der Wochentag N Tage zuvor? In Python können wir mit berechnen

return (2 - N) % 7

aber in C, wenn N ≥ 3 ist, erhalten wir eine negative Zahl, die eine ungültige Zahl ist, und wir müssen sie manuell korrigieren, indem wir 7 hinzufügen:

int result = (2 - N) % 7;
return result < 0 ? result + 7 : result;

(Unter http://en.wikipedia.org/wiki/Modulo_operator erfahren Sie, wie das Vorzeichen des Ergebnisses für verschiedene Sprachen bestimmt wird.)

kennytm
quelle
6
Überraschenderweise gibt Pythons Modulo-Operator (%) nicht immer eine Zahl mit demselben Vorzeichen wie der Nenner (Divisor) zurück. Siehe stackoverflow.com/questions/48347515/…
zezollo
33

Hier ist eine Erklärung von Guido van Rossum:

http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html

Im Wesentlichen ist es so, dass a / b = q mit dem Rest r die Beziehungen b * q + r = a und 0 <= r <b beibehält.

Kevin
quelle
4
Sprachen wie C ++ und Java behalten ebenfalls die erste Beziehung bei, aber sie sind negativ a, positiv b, während Python-Stockwerke. Es ist immer wahr abs(r) < b, und sie decken iff r <= 0.
Evgeni Sergeev
9

Es gibt keinen besten Weg, um mit Ganzzahldivision und Mods mit negativen Zahlen umzugehen. Es wäre schön, wenn a/bes die gleiche Größe und das entgegengesetzte Vorzeichen von wäre (-a)/b. Es wäre schön, wenn a % bes tatsächlich ein Modulo b wäre. Da wir wirklich wollen a == (a/b)*b + a%b, sind die ersten beiden nicht kompatibel.

Welche zu behalten ist, ist eine schwierige Frage, und es gibt Argumente für beide Seiten. C und C ++ runden die Ganzzahldivision gegen Null (so a/b == -((-a)/b)), und Python anscheinend nicht.

David Thornley
quelle
1
"Es wäre schön, wenn a / b die gleiche Größe und das entgegengesetzte Vorzeichen von (-a) / b hätte." Warum sollte das schön sein? Wann ist das ein gewünschtes Verhalten?
user76284
Denn es würde sich dann genauso verhalten wie reguläre Division und Multiplikation und ist daher intuitiv einfach zu handhaben. Das mag aber mathematisch nicht sinnvoll sein.
Demis
5

Wie bereits erwähnt, macht Python modulo eine gut begründete Ausnahme von den Konventionen anderer Sprachen.

Dies gibt negativen Zahlen ein nahtloses Verhalten, insbesondere wenn sie in Kombination mit dem //Integer-Divide-Operator verwendet werden, wie es %Modulo häufig ist (wie in math. Divmod ):

for n in range(-8,8):
    print n, n//4, n%4

Produziert:

 -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 %gibt immer Null oder Positiv aus, wenn der Divisor positiv ist
  • Python //rundet immer in Richtung negative Unendlichkeit
Bob Stein
quelle
Dank Ihres Beispiels habe ich es verstanden :)
Lamis
5

In Python funktioniert der Modulo-Operator folgendermaßen.

>>> mod = n - math.floor(n/base) * base

Das Ergebnis ist also (für Ihren Fall):

mod = -5 - floor(-1.25) * 4
mod = -5 - (-2*4)
mod = 3

Während andere Sprachen wie C, JAVA und JavaScript die Kürzung anstelle von Floor verwenden.

>>> mod = n - int(n/base) * base

was in ... endet:

mod = -5 - int(-1.25) * 4
mod = -5 - (-1*4)
mod = -1

Wenn Sie weitere Informationen zum Runden in Python benötigen, lesen Sie diese .

Munkhbold Enkhtur
quelle
3

Modulo, Äquivalenzklassen für 4:

  • 0: 0, 4, 8, 12 ... und -4, -8, -12 ...
  • 1: 1, 5, 9, 13 ... und -3, -7, -11 ...
  • 2: 2, 6, 10 ... und -2, -6, -10 ...
  • 3: 3, 7, 11 ... und -1, -5, -9 ...

Hier ist ein Link zu Modulos Verhalten mit negativen Zahlen . (Ja, ich habe gegoogelt)

Wheaties
quelle
@NullUserException - ja, das war es. Fest. Vielen Dank.
Wheaties
1

Ich dachte auch, dass es ein seltsames Verhalten von Python war. Es stellt sich heraus, dass ich die Trennung nicht gut gelöst habe (auf dem Papier); Ich gab dem Quotienten einen Wert von 0 und dem Rest einen Wert von -5. Schrecklich ... Ich habe die geometrische Darstellung von Ganzzahlen vergessen. Durch Abrufen der Geometrie der Ganzzahlen, die durch die Zahlenlinie angegeben werden, können die korrekten Werte für den Quotienten und den Rest ermittelt und überprüft werden, ob das Verhalten von Python in Ordnung ist. (Obwohl ich davon ausgehe, dass Sie Ihr Problem bereits vor langer Zeit gelöst haben).

joser
quelle
1

Erwähnenswert ist auch, dass sich auch die Unterteilung in Python von C: Consider unterscheidet

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

in C erwarten Sie das Ergebnis

0

Was ist x / y in Python?

>>> print x/y
-1

und% ist modulo - nicht der Rest! Während x% y in C ergibt

-10

Python ergibt.

>>> print x%y
27

Sie können beide wie in C erhalten

Der Unternehmensbereich:

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

Und der Rest (unter Verwendung der Unterteilung von oben):

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

Diese Berechnung ist möglicherweise nicht die schnellste, funktioniert jedoch für alle Vorzeichenkombinationen von x und y, um die gleichen Ergebnisse wie in C zu erzielen. Außerdem werden bedingte Anweisungen vermieden.

bebbo
quelle