Einer meiner Lieblingshasse von C-abgeleiteten Sprachen (als Mathematiker) ist das
(-1) % 8 // comes out as -1, and not 7
fmodf(-1,8) // fails similarly
Was ist die beste Lösung?
C ++ ermöglicht die Möglichkeit des Überladens von Vorlagen und Operatoren, aber beide sind für mich trübes Wasser. Beispiele dankbar erhalten.
%
soll das Modulo sein ... es ist der Rest .%
Problem.(-1) & 8 == 7
Antworten:
Zunächst möchte ich darauf hinweisen, dass Sie sich nicht einmal darauf verlassen können, dass
(-1) % 8 == -1
. Das einzige, worauf Sie sich verlassen können, ist das(x / y) * y + ( x % y) == x
. Ob der Rest jedoch negativ ist oder nicht, ist implementierungsdefiniert .Warum nun hier Vorlagen verwenden? Eine Überlastung für Ints und Longs würde genügen.
und jetzt kannst du es wie mod (-1,8) nennen und es scheint 7 zu sein.
Bearbeiten: Ich habe einen Fehler in meinem Code gefunden. Es wird nicht funktionieren, wenn b negativ ist. Also ich denke das ist besser:
Referenz: C ++ 03 Absatz 5.6 Abschnitt 4:
quelle
INT_MIN / -1
(bei Zweierkomplementimplementierungen). Unter der alten Spezifikation muss-32768 % -1
möglicherweise ausgewertet werden-65536
(was auch nicht im Bereich des 16-Bit-Typs liegt, yuck!), Damit die Identität erhalten bleibt.Hier ist eine C-Funktion, die positive ODER negative Ganzzahl- ODER Bruchwerte für BEIDE OPERANDEN verarbeitet
Dies ist aus mathematischer Sicht sicherlich die eleganteste Lösung. Ich bin mir jedoch nicht sicher, ob es robust im Umgang mit ganzen Zahlen ist. Manchmal schleichen sich beim Konvertieren von int -> fp -> int Gleitkommafehler ein.
Ich verwende diesen Code für Nicht-Ints und eine separate Funktion für Int.
HINWEIS: muss N = 0 abfangen!
Testcode:
(Hinweis: Sie können es direkt über CodePad kompilieren und ausführen: http://codepad.org/UOgEqAMA )
Ausgabe:
quelle
floor()
. Außerdem können Sie beim Konvertieren in Float an Präzision verlieren: Versuchen Sie(float)1000000001/3
, Sie werden von den Ergebnissen überrascht sein!Ich habe gerade bemerkt, dass Bjarne Stroustrup
%
als Restoperator und nicht als Modulooperator bezeichnet.Ich würde wetten, dass dies der formale Name in den ANSI C & C ++ - Spezifikationen ist und dass sich ein Missbrauch der Terminologie eingeschlichen hat. Weiß jemand dies für eine Tatsache?
Wenn dies jedoch der Fall ist, sind die Funktionen fmodf () von C (und wahrscheinlich auch andere) sehr irreführend. Sie sollten mit fremf () usw. bezeichnet werden
quelle
%
) erwähnt.Die einfachste allgemeine Funktion, um das positive Modulo zu finden, wäre folgende: Es würde sowohl für positive als auch für negative Werte von x funktionieren.
quelle
Für ganze Zahlen ist dies einfach. Mach einfach
wo ich annehme,
N
ist das positiv und darstellbar in der Art vonx
. Ihr Lieblings-Compiler sollte dies optimieren können, sodass es in Assembler nur zu einer Mod-Operation kommt.quelle
int x=-9001; unsigned int N=2000;
es gibt 2295, nicht 999.(x < 0) ? (x % N + N) : (x % N)
.Die beste Lösung für einen Mathematiker ist die Verwendung von Python.
Das Überladen von C ++ - Operatoren hat wenig damit zu tun. Sie können Operatoren für integrierte Typen nicht überladen. Was Sie wollen, ist einfach eine Funktion. Natürlich können Sie C ++ - Vorlagen verwenden, um diese Funktion für alle relevanten Typen mit nur 1 Code zu implementieren.
Die Standard-C-Bibliothek bietet
fmod
, wenn ich mich richtig an den Namen erinnere, Gleitkommatypen.Für ganze Zahlen können Sie eine C ++ - Funktionsvorlage definieren, die immer einen nicht negativen Rest (entsprechend der euklidischen Division) als ... zurückgibt.
... und schreibe einfach
mod(a, b)
statta%b
.Hier muss der Typ
Integer
ein vorzeichenbehafteter Integer-Typ sein.Wenn Sie das übliche mathematische Verhalten wünschen, bei dem das Vorzeichen des Restes mit dem Vorzeichen des Divisors übereinstimmt, können Sie z
… Mit der gleichen Einschränkung
Integer
, dass es sich um einen signierten Typ handelt.¹ Weil Pythons ganzzahlige Division in Richtung negativer Unendlichkeit rundet.
quelle
r
Ergebnis mussa
=r + b*(a/b)
true machen. Unabhängig davon, wie die Ganzzahldivision implementiert ist,b*something
ist das ein Vielfaches vonb
. Diesr
ergibt ein gültiges Modulo-Ergebnis, auch wenn es negativ ist. Sie können abs (b
) hinzufügen und es wird immer noch ein gültiges Modulo-Ergebnis sein.Oh, ich hasse% Design auch dafür ....
Sie können die Dividende folgendermaßen in eine nicht unterzeichnete Dividende umwandeln:
Wenn der Versatz dem (-INT_MIN) Vielfachen des Moduls am nächsten kommt, ändert das Addieren und Subtrahieren des Moduls das Modulo nicht. Beachten Sie, dass der Typ ohne Vorzeichen und das Ergebnis eine Ganzzahl sind. Leider können die Werte INT_MIN ... (- offset-1) nicht korrekt konvertiert werden, da sie einen arifmetischen Überlauf verursachen. Diese Methode bietet jedoch nur eine zusätzliche Arithmetik pro Operation (und keine Bedingungen), wenn mit einem konstanten Teiler gearbeitet wird, sodass sie in DSP-ähnlichen Anwendungen verwendet werden kann.
Es gibt einen Sonderfall, in dem der Teiler 2 N (ganzzahlige Zweierpotenz) ist, für den Modulo mit einfacher arithmetischer und bitweiser Logik berechnet werden kann
beispielsweise
Häufiger und weniger schwierig ist es, Modulo mit dieser Funktion zu erhalten (funktioniert nur mit positivem Teiler):
Dies ist nur das richtige Ergebnis, wenn es negativ ist.
Sie können auch tricksen:
(p% q + q)% q
Es ist sehr kurz, aber verwenden Sie zwei% -s, die normalerweise langsam sind.
quelle
Ich glaube, eine andere Lösung für dieses Problem wäre die Verwendung von Variablen vom Typ long anstelle von int.
Ich habe gerade an einem Code gearbeitet, bei dem der% -Operator einen negativen Wert zurückgegeben hat, der einige Probleme verursacht hat (zum Generieren einheitlicher Zufallsvariablen für [0,1] möchten Sie nicht wirklich negative Zahlen :)), aber nach dem Umschalten der Variablen auf Typ lang, alles lief reibungslos und die Ergebnisse stimmten mit denen überein, die ich beim Ausführen des gleichen Codes in Python erhielt (wichtig für mich, da ich in der Lage sein wollte, die gleichen "Zufallszahlen" auf mehreren Plattformen zu generieren.
quelle
Hier ist eine neue Antwort auf eine alte Frage, die auf diesem Microsoft Research-Dokument und den darin enthaltenen Referenzen basiert .
Man beachte , dass von C11 und C ++ 11 ab, die Semantik
div
worden Trunkierung gegen Null (siehe[expr.mul]/4
). Darüber hinaus garantiert C ++ 11 fürD
dividiert durchd
Folgendes den QuotientenqT
und den RestrT
Dabei
signum
werden -1, 0, +1 zugeordnet, je nachdem, ob das Argument <, ==,> als 0 ist ( Quellcode finden Sie in diesen Fragen und Antworten).Bei abgeschnittener Division ist das Vorzeichen des Restes gleich dem Vorzeichen der Dividende
D
, d-1 % 8 == -1
. H. C ++ 11 bietet auch einestd::div
Funktion, die eine Struktur mit Elementenquot
undrem
entsprechend der abgeschnittenen Unterteilung zurückgibt .Es gibt noch andere Definitionen möglich, zB sogenannte Boden Teilung kann in Bezug auf die builtin abgeschnitten Teilung definiert werden
Bei einer Bodenteilung entspricht das Vorzeichen des Restes dem Vorzeichen des Teilers
d
. In Sprachen wie Haskell und Oberon gibt es eingebaute Operatoren für die Teilung von Fußböden. In C ++ müssen Sie eine Funktion mit den obigen Definitionen schreiben.Ein weiterer Weg ist die euklidische Teilung , die auch als eingebaute abgeschnittene Teilung definiert werden kann
Bei der euklidischen Teilung ist das Vorzeichen des Restes immer positiv .
quelle
Für eine Lösung, die keine Zweige und nur 1 Mod verwendet, können Sie Folgendes tun
quelle
... oder gewöhnen Sie sich einfach daran, einen Vertreter für die Äquivalenzklasse zu finden.
quelle
r
. Der%
Operator hat nichts mit Äquivalenzklassen zu tun. Es ist der Restoperator und der Rest ist algebraisch gut definiert, um nicht negativ und kleiner als der Divisor zu sein. Leider hat C es falsch definiert. Trotzdem +1 für eine der besten Antworten.Beispielvorlage für C ++
Mit dieser Vorlage ist der zurückgegebene Rest Null oder hat das gleiche Vorzeichen wie der Divisor (Nenner) (das Äquivalent zur Rundung auf negative Unendlichkeit), anstatt dass das C ++ - Verhalten des Restes Null ist oder das gleiche Vorzeichen wie die Dividende hat ( Zähler) (entspricht der Rundung gegen Null).
quelle
quelle
quelle
Diese Lösung (zur Verwendung, wenn sie
mod
positiv ist) vermeidet es, alle negativen Teilungs- oder Restoperationen zusammen durchzuführen:quelle
Ich würde tun:
Dies addiert die letztere Zahl zur ersten, bevor das Modulo wie gewünscht 7 ergibt. Dies sollte für jede Zahl bis zu -8 funktionieren. Für -9 addiere 2 * 8.
quelle
-99999
?