Ich habe Probleme, zu verstehen, wie und warum dieser Code so funktioniert. Mein Partner in dieser Aufgabe beendete diesen Teil und ich kann ihn nicht finden, um herauszufinden, wie und warum das funktioniert. Ich habe ein paar verschiedene Dinge versucht, um es zu verstehen, aber jede Hilfe würde sehr geschätzt werden. Dieser Code verwendet 2er-Komplement und eine 32-Bit-Darstellung.
%Vor% Hier wird berechnet, wie viele höherwertige Bits nach der Verwendung von n
niederwertigen Bits übrig bleiben.
Dies füllt die höherwertigen Bits mit dem gleichen Wert wie das Vorzeichenbit von x
.
Dies entspricht
%Vor% Auf einer 2er-Komplement-Plattform entspricht -n
~n + 1
. Aus diesem Grund entspricht c = 33 + ~n
auf einer solchen Plattform tatsächlich c = 32 - n
. Diese c
soll darstellen, wieviele höherwertige Bits in einem 32-Bit int
-Wert verbleiben, wenn n
untere Bits belegt sind.
Beachten Sie zwei Teile der Plattformabhängigkeit in diesem Code: 2's-Komplement-Plattform, 32-Bit int
type.
Dann soll ((x << c) >> c
diese c
Bits höherer Ordnung sign-fill signieren. Sign-Fill bedeutet, dass diejenigen Werte von x
, die 0
in Bit-Position n - 1
haben, diese höherwertigen Bits auf Null gesetzt werden müssen. Aber für die Werte von x
, die 1
in Bit-Position n - 1
haben, müssen diese höherwertigen Bits mit 1
s gefüllt werden. Dies ist wichtig, damit der Code für negative Werte von x
ordnungsgemäß funktioniert.
Dies führt zwei weitere Teile der Plattformabhängigkeit ein: <<
Operator, der sich beim Verschieben negativer Werte gut verhält oder wenn 1
in das Vorzeichenbit (formal ist dies ein undefiniertes Verhalten) und >>
Operator, der Zeichen vorschreibt Erweiterung beim Verschieben negativer Werte (formal ist es implementierungsdefiniert)
Der Rest ist, wie oben beantwortet, nur ein Vergleich mit dem ursprünglichen Wert von x
: !(a ^ b)
entspricht a == b
. Wenn die obigen Transformationen den ursprünglichen Wert von x
nicht zerstören, dann passt x
tatsächlich in n
niedrigere Bits der 2'-Komplement-Darstellung.
Die Verwendung des bitweisen Komplementoperators (unary ~
) für eine vorzeichenbehaftete Ganzzahl hat implementierungsdefinierte und undefinierte Aspekte . Mit anderen Worten, dieser Code ist nicht portierbar, auch wenn Sie nur Zweierkomplement-Implementierungen in Betracht ziehen.
Es ist wichtig zu beachten, dass sogar Zweierkomplementdarstellungen in C Trap-Repräsentationen haben können. 6.2.6.2p2 sagt sogar das ganz klar:
Wenn das Vorzeichenbit eins ist, muss der Wert auf eine der folgenden Arten geändert werden:
- der entsprechende Wert mit Vorzeichenbit 0 wird negiert (Vorzeichen und Betrag);
- das Vorzeichenbit hat den Wert - (2 M) (Zweierkomplement);
- das Vorzeichenbit hat den Wert - (2 M - 1) (Einerkomplement).
Welche davon gilt, ist implementierungsdefiniert, wie ist der Wert mit Vorzeichenbit 1 und allen Wertbits Null (für die ersten beiden) oder mit Vorzeichenbit und allen Wertbits 1 ( Für das Einerkomplement ist eine Trap-Repräsentation oder ein normaler Wert.
Der Schwerpunkt liegt bei mir. Die Verwendung von Trap-Repräsentationen ist ein undefiniertes Verhalten .
Es gibt tatsächliche Implementierungen, die diesen Wert im Standardmodus als Trap-Repräsentation reservieren. Der bemerkenswerteste, den ich zitiere, ist Unisys Clearpath Dordado auf OS2200 (gehe zu 2-29) . Notieren Sie das Datum auf diesem Dokument; solche Implementierungen sind nicht unbedingt alt (daher der Grund, warum ich diesen zitiere).
Laut 6.2.6.2p4 ist das Verschieben negativer Werte nach links ebenfalls ein undefiniertes Verhalten. Ich habe nicht viele Nachforschungen darüber angestellt, welche Verhaltensweisen es in der Realität gibt, aber ich würde vernünftigerweise erwarten, dass es Implementierungen gibt, die sich unterschreiben, sowie Implementierungen, die dies nicht tun. Dies wäre auch eine Möglichkeit, die oben erwähnten Trap-Repräsentationen zu bilden, die in der Natur undefiniert und daher unerwünscht sind. Theoretisch (oder vielleicht einige Zeit in der fernen oder nicht so fernen Zukunft) könnten Sie auch Signale sehen, die "einer rechnerischen Ausnahme entsprechen" (das ist eine C-Standardkategorie, ähnlich der, in die SIGSEGV
fällt "Division durch Null") oder auf andere Weise fehlerhafte und / oder unerwünschte Verhaltensweisen ...
Fazit: Der einzige Grund dafür, dass der Code in der Frage funktioniert, ist der Zufall, dass die Entscheidungen, die Ihre Implementierung getroffen hat, sich auf die richtige Weise ergeben. Wenn Sie die Implementierung verwenden, die ich aufgelistet habe, werden Sie wahrscheinlich feststellen, dass dieser Code nicht wie erwartet für einige Werte funktioniert.
Solche schwere Zauberei (wie sie in den Kommentaren beschrieben wurde) ist nicht wirklich notwendig und sieht für mich nicht so optimal aus. Wenn Sie etwas wollen, das nicht auf magic angewiesen ist (z. B. etwas portables), um dieses Problem zu lösen, ziehen Sie es in Betracht (tatsächlich funktioniert dieser Code für mindestens 1 <= n <= 64
):
Tags und Links c bit-manipulation bit-shift bitwise-operators twos-complement