2008-09-04 11 views
15

Können Sie mir bitte sagen, wie viel ist (-2) % 5? Laut meinem Python-Interpreter ist das 3, aber hast du eine kluge Erklärung dafür?Moduloperation mit negativen Werten - seltsame Sache?

Ich habe gelesen, dass das Ergebnis in einigen Sprachen maschinenabhängig sein kann, aber ich bin mir nicht sicher.

+0

Sie können 'math.fmod' verwenden, um das gleiche Verhalten wie in C oder Java zu erhalten. – Helio

Antwort

16

Übrigens: Die meisten Programmiersprachen stimmen mit Python nicht überein und geben das Ergebnis -2. Abhängig von der Interpretation des Moduls ist dies korrekt. Allerdings ist die vereinbarte mathematische Definition besagt, dass das Modul von a und b die (streng positiv) ist Ruhe r der Division von a /b. Genauer gesagt 0 < = r < b per definitionem.

11

Ihr Python-Interpreter ist korrekt. Eine (dumme) Methode zur Berechnung eines Moduls besteht darin, den Modulus zu subtrahieren oder zu addieren, bis der resultierende Wert zwischen 0 und (Modul - 1) liegt.

Beispiel: 13 mod 5 = (13-5) mod 5 = (13-10) mod 5 = 3

oder im Fall: -2 mod 5 = (-2 + 5) mod 5 = 3

+1

Es hängt davon ab, wie die Integer-Division definiert ist. Wenn Division auf 0 rollt, dann sollte der Rest das gleiche Vorzeichen haben wie der Quotient, so dass "b * (a/b) + a% b == a". Wenn Division auf negative Unendlichkeit (wie in Python) rundet, sollte der Rest immer positiv sein. – augurar

0

Nun, -2 dividiert durch 5 wäre 0 mit einem Rest von 3. Ich glaube nicht, dass sollte sehr plattformabhängig sein, aber ich habe fremde Dinge gesehen.

+3

Sie meinen sicherlich: "-2 dividiert durch 5 wäre -1 mit einem Rest von 3", richtig? Das ist es, was Python sowieso macht. – tzot

+0

In einigen Systemen ist -2 dividiert durch 5 0 mit einem Rest von -2. Bei anderen ist es -1 mit einem Rest von 3. Nur eine Frage des Geschmacks, es gibt keine "richtige" Antwort. – augurar

0

Es ist in der Tat 3. In modular arithmetic, ist ein Modul einfach der Rest einer Division, und der Rest von -2 geteilt durch 5 3.

+0

In dem Ausdruck "c = a% b" ist "b" der Modulus, nicht "c". – augurar

4

Nun, 0% 5 sollte 0 sein, oder?

-1% 5 sollte 4 sein, weil das die nächste erlaubte Ziffer in umgekehrter Richtung ist (d. H. Es kann nicht 5 sein, da das außerhalb des Bereichs liegt).

Und von dieser Logik folgend entlang, -2 muss 3 seine

Der einfachste Weg, um zu denken, wie es funktioniert, ist, dass Sie immer das Hinzufügen oder 5 abgezogen, bis die Zahl zwischen 0 (einschließlich) fällt und 5 (exklusiv).

Ich bin mir nicht sicher über Maschinenabhängigkeit - ich habe noch nie eine Implementierung gesehen, aber ich kann nicht sagen, dass es nie gemacht wurde.

0

Das Ergebnis hängt von der Sprache ab. Python gibt das Vorzeichen des Divisors zurück, wobei beispielsweise C# das Vorzeichen des Dividenden zurückgibt (dh -2% 5 gibt -2 in C# zurück).

0

Eine Erklärung könnte sein, dass negative Zahlen mit 2's complement gespeichert werden. Wenn der Python-Interpreter versucht, die Modulo-Operation auszuführen, wird er in einen vorzeichenlosen Wert konvertiert. Anstelle von (-2)% 5 berechnet es tatsächlich 0xFFFF_FFFF_FFFF_FFFD% 5, was 3 ist.

4

Wie in anderen Antworten erklärt, gibt es viele Möglichkeiten für eine Modulo-Operation mit negativen Werten.Im Allgemeinen werden verschiedene Sprachen (und unterschiedliche Maschinenarchitekturen) zu einem anderen Ergebnis führen.

Nach den Python reference manual,

der Modulo-Operator immer ein Ergebnis mit dem gleichen Vorzeichen wie dem zweiten Operanden ergibt (oder Null); der absolute Wert des Ergebnisses ist streng kleiner als der absolute Wert des zweiten Operanden.

ist die Wahl von Python getroffen. Grundsätzlich Modulo definiert ist, so dass diese immer gilt:

x == (x/y)*y + (x%y) 

so macht es Sinn, dass (-2)% 5 = -2 - (-2/5) * 5 = 3

0

vorsichtig sein, nicht zu Verlasse dich auf dieses Mod-Verhalten in C/C++ auf allen Betriebssystemen und Architekturen. Wenn ich mich richtig erinnere, habe ich versucht, wie auf C/C++ Code verlassen

float x2 = x % n; 

x2 im Bereich von 0 bis n-1, aber negativen Zahlen schleichen in zu halten, wenn ich auf einem O kompilieren würde, aber die Dinge würden funktioniert gut auf einem anderen Betriebssystem. Dies führte zu einem bösen Debugging, da es nur die halbe Zeit passierte!

6

Wie die Dokumentation sagt in Binary arithmetic operations, versichert Python, dass:

Die Integer-Division und Modulo-Operatoren durch die folgende Identität verbunden sind: x == (x/y)*y + (x%y). Integer division und modulo sind auch mit der eingebauten Funktion divmod() verbunden: divmod(x, y) == (x/y, x%y).

Und wirklich,

>>> divmod(-2, 5) 
(-1, 3). 

Ein anderer Weg, um die Einheitlichkeit dieser Methode sichtbar zu machen ist divmod für eine kleine Folge von Zahlen zu berechnen:

>>> for number in xrange(-10, 10): 
...  print divmod(number, 5) 
... 
(-2, 0) 
(-2, 1) 
(-2, 2) 
(-2, 3) 
(-2, 4) 
(-1, 0) 
(-1, 1) 
(-1, 2) 
(-1, 3) 
(-1, 4) 
(0, 0) 
(0, 1) 
(0, 2) 
(0, 3) 
(0, 4) 
(1, 0) 
(1, 1) 
(1, 2) 
(1, 3) 
(1, 4) 
0

Es scheint eine gemeinsame Verwirrung zu sein zwischen den Begriffen "Modulo" und "Rest".

In der Mathematik sollte ein Rest immer mit dem Quotienten konsistent definiert werden, so dass, wenn a/b == c rem d dann (c * b) + d == a. Abhängig davon, wie Sie Ihren Quotienten runden, erhalten Sie unterschiedliche Reste.

modulo sollte jedoch immer ein Ergebnis 0 <= r < divisor ergeben, das nur dann konsistent ist, wenn die negativen Zahlen ganzzahlig sind. Wenn die Division in Richtung Null läuft (was üblich ist), sind Modulo und Rest nur äquivalent für nicht-negative Werte.

Einige Sprachen (insbesondere C und C++) definieren nicht die erforderlichen Rundungs-/Restverhalten und % ist mehrdeutig. Viele definieren die Rundung als gegen Null, aber verwenden Sie den Ausdruck modulo, wo der Rest richtiger wäre. Python ist insofern relativ ungewöhnlich, als es auf die negative Unendlichkeit rundet, so dass Modulo und Rest äquivalent sind.

Ada Runden gegen Null IIRC, hat aber beide mod und rem Betreiber.

Die C-Richtlinie soll es Compilern ermöglichen, die effizienteste Implementierung für die Maschine zu wählen, aber IMO ist zumindest in diesen Tagen eine falsche Optimierung. Ein guter Compiler wird wahrscheinlich in der Lage sein, die Äquivalenz für die Optimierung zu verwenden, wo immer eine negative Zahl nicht auftreten kann (und fast sicher, wenn Sie unsignierte Typen verwenden). Auf der anderen Seite, wo negative Zahlen auftreten können, kümmern Sie sich fast sicher um die Details - aus Gründen der Portabilität müssen Sie sehr sorgfältig entworfene überkomplexe Algorithmen und/oder Prüfungen verwenden, um sicherzustellen, dass Sie unabhängig von Rundung und Rest die gewünschten Ergebnisse erhalten Verhalten. Mit anderen Worten, der Gewinn für diese "Optimierung" ist meistens (wenn auch nicht immer) eine Illusion, während es in einigen Fällen sehr reale Kosten gibt - also ist es eine falsche Optimierung.

Verwandte Themen