Wie werden negative Zahlen dargestellt -- Vorzeichen-Betrag, Einerkomplement, Zweierkomplement
In dieser Lektion wirst du verstehen: warum die Darstellung negativer Zahlen in Computern nicht so einfach ist wie das "Hinzufügen eines Minuszeichens", und wie das Zweierkomplement all diese Probleme auf clevere Weise löst.
Alltagsanalogie: Nummerierung von Hotel-Etagen
Stell dir ein Hotel mit 256 Etagen vor.
Wenn man mit natürlichen Zahlen nummeriert, kann man nur 0 bis 255 verwenden.
Aber was, wenn wir von "128 Etagen unter der Erde" bis "127 Etagen über der Erde" nummerieren wollen?
Ein cleverer Ansatz: Interpretiere die Zahlen 128~255 als -128~-1 neu.
Die Zahl 128 wird zu -128, die Zahl 255 wird zu -1, und die Zahl 0 bleibt 0.
Dies istKernidee des Zweierkomplements— Innerhalb eines endlichen Bereichs wird die "hintere Hälfte" des Wertebereichs als negativ neu interpretiert.
Die einfachste Idee: Vorzeichen-Betrag (Originalcode)
Die direkteste Idee zur Darstellung negativer Zahlen: das höchste Bit alsVorzeichenbit verwenden。
- Höchstes Bit 0 = positive Zahl
- Höchstes Bit 1 = negative Zahl
- Die restlichen Bits stellen den Absolutwert dar
Nehmen wir 8 Bits als Beispiel:
Klingt vernünftig, oder? Aber Vorzeichen-Betrag hat zwei fatale Probleme.
Problem eins: +0 und -0 existieren beide.
Positive 0 ist00000000, negative 0 ist10000000。
Der gleiche Wert hat zwei Darstellungen.
Das bedeutet, dass jede Vergleichsoperation (Prüfen, ob zwei Zahlen gleich sind) erfordert, dass die Hardware zusätzlich diesen Sonderfall behandelt.
Problem 2: Der Addierer kann nicht subtrahieren
Berechnung mit Vorzeichen-Betrag5 + (-3):
5 + (-3) sollte 2 ergeben, aber direkte Addition mit Vorzeichen-Betrag ergibt -8.
Das bedeutet, dass die CPU separat eine Subtraktionsschaltung entwerfen muss, anstatt die vorhandene Additionsschaltung wiederzuverwenden.
Das Problem mit der Vorzeichen-Betrag-Darstellung lässt sich so zusammenfassen: intuitiv, aber nicht praktikabel. Sie eignet sich für Menschen zum Lesen, aber nicht für die Schaltungsimplementierung.
Die Offenbarung der Uhr: Modulare Arithmetik
Bevor wir fortfahren, müssen wir ein Schlüsselkonzept verstehen –Modulo-Operation。
Betrachten Sie eine Uhr: Das Zifferblatt hat nur 12 Markierungen.
Ausgehend von 10 Uhr möchten Sie auf 7 Uhr einstellen. Es gibt zwei Möglichkeiten:
Methode eins (gegen den Uhrzeigersinn): 10 - 3 =7(Subtraktion)
Methode 2 (im Uhrzeigersinn): 10 + 9 = 19, 19 mod 12 =7(Addition)
In einem „Modulo-12“-System sind -3 und +9 äquivalent (weil -3 + 12 = 9).
Allgemeiner:In einem Modulo-M-System ist das Subtrahieren einer Zahl äquivalent zum Addieren von (M - diese Zahl)。
Das Zweierkomplement wendet diese Idee auf Binärzahlen an. Für 8-Bit-Binärzahlen ist der Modul 2^8 = 256.
Das Zweierkomplement von -3 = 256 - 3 = 253 = binär.11111101。
therefore5 + (-3)Kann werden5 + 253 = 258。
Aber in einem 8-Bit-System überschreitet 258 die Obergrenze von 255. Der Übertrag vom höchsten Bit „läuft über“ und wird verworfen, sodass258 mod 256 = 2。
Die Addition ergab das korrekte Ergebnis der Subtraktion.
Zweierkomplement-Ringdiagramm (interaktive Demo)
Das Ringdiagramm unten zeigt intuitiv ein 4-Bit-Zweierkomplement-System.
4 Bits können 16 Werte darstellen (0~15); das Zweierkomplement interpretiert sie als -8~+7.
Ziehe den Schieberegler, um den Wert zu ändern, und beobachte die Position des Zeigers auf dem Ring.
Zweierkomplement-Ringdiagramm (4-Bit-System, Bereich -8 ~ +7)
Binärdarstellung:0000| Vorzeichenlose Interpretation:0
Plotly-Zweierkomplement-Ringdiagramm (scatterpolar-Polarkoordinaten).
Die folgende Abbildung verwendet Plotly.js-Polarkoordinaten, um die 16 Teilstriche des 4-Bit-Zweierkomplements anzuzeigen. Blau = positiver Bereich, Rot = negativer Bereich, Grün = Nullpunkt.
Bedeutung von zyklisch:
- Richtung im Uhrzeigersinn = Wert nimmt zu
- Einmal im Kreis gehen (Überschreiten des Maximums) = Rückkehr zum Ausgangspunkt (der „Überlauf“-Effekt der modularen Arithmetik)
- Roter Bereich = negative Zahlen (Zweierkomplement-Interpretation), blauer Bereich = positive Zahlen
- Der innere Ring ist mit der vorzeichenlosen Interpretation beschriftet (entspricht 0~15), der äußere Ring ist mit der Zweierkomplement-Interpretation beschriftet (-8~+7)
Aus dem Diagramm ist ersichtlich, dassdas Zweierkomplement den Wertebereich im Wesentlichen zu einem Ring faltet.。
Wenn positive Zahlen das Ende erreichen (nach +7), werden sie natürlicherweise negativ (-8). Subtraktion und Addition verlaufen auf dem Ring in derselben Richtung.
Berechnungsregeln für das Zweierkomplement
Um das Zweierkomplement einer Zahl manuell zu berechnen, gibt es zwei äquivalente Schnellmethoden:
Methode eins: invertieren und 1 addieren (Standardmethode)
- Schreibe die dem Absolutwert entsprechende Binärdarstellung
- Invertiere jedes Bit (0 wird 1, 1 wird 0), um das Einerkomplement zu erhalten
- Addiere 1 zum Einerkomplement, um das Zweierkomplement zu erhalten
Methode zwei: von rechts nach links (Schnellberechnungsmethode)
- Finde die erste 1 von rechts nach links
- Invertiere alle Bits links von dieser 1
- Diese 1 und alle Bits rechts davon bleiben unverändert
Nimm -20 als Beispiel (8-Bit-System) und überprüfe mit beiden Methoden:
Überprüfung: Das Zweierkomplement von -20 ist 11101100; als vorzeichenlos interpretiert, ergibt es 236.
236 + 20 = 256, genau ein Modulus. Das zeigt, dass die Berechnung korrekt ist.
Vergleich der drei Darstellungen
| value | Vorzeichen-Betrag-Darstellung | Einerkomplement | Zweierkomplement |
|---|---|---|---|
| +5 | 00000101 | 00000101 | 00000101 |
| -5 | 10000101 | 11111010 | 11111011 |
| +0 | 00000000 | 00000000 | 00000000 |
| -0 | 10000000 | 11111111 | Existiert nicht |
| +127 | 01111111 | 01111111 | 01111111 |
| -127 | 11111111 | 10000000 | 10000001 |
| -128 | Kann nicht dargestellt werden | Kann nicht dargestellt werden | 10000000 |
Beachten Sie drei wichtige Unterschiede:
- Zweierkomplement hat keine -0: Alle Einsen werden nicht mehr für -0 verschwendet, sondern stellen -1 dar.
- Zweierkomplement hat eine zusätzliche negative ZahlDer Wertebereich des 8-Bit-Zweierkomplements ist -128 ~ +127, was ein -128 mehr enthält als der Wertebereich der Vorzeichen-Betrag-Darstellung von -127 ~ +127.
- Zweierkomplement vereinheitlicht die Addition: Die Addition positiver und negativer Zahlen verwendet dieselbe Schaltung.
Deshalb ist Javas
byteDer Typbereich ist -128~127, und der von Csigned charist ebenfalls -128~127. Beide verwenden das 8-Bit-Zweierkomplement.
Interaktive Demo: Zweierkomplement-Rechner
Geben Sie unten eine Ganzzahl im 8-Bit-Bereich (-128 ~ 127) ein, um ihre Vorzeichen-Betrag-Darstellung, ihr Einerkomplement und ihr Zweierkomplement anzuzeigen.
Zweierkomplement-Rechner (8-Bit)
Besonderheit: Wenn -128 eingegeben wird, lautet das Zweierkomplement10000000。
Der Absolutbetrag dieser Zahl ist 128, aber 128 liegt außerhalb des positiven 8-Bit-Bereichs (0~127).
Daher hat -128 keine entsprechende positive Darstellung—dies ist eine interessante Eigenschaft des Zweierkomplementsystems.
Zweierkomplement-Überlauf
Der Bereich des 8-Bit-Zweierkomplements ist -128~127.
Wenn das Berechnungsergebnis diesen Bereich überschreitet, wird es einenÜberlauf verursachen。
Dies erinnert wieder an das frühere Ringdiagramm – nach Erreichen des Endes „läuft der Wert um“.
In C/C++ ist der Überlauf von vorzeichenbehafteten Ganzzahlenundefiniertes Verhalten(der Compiler darf annehmen, dass er nie auftritt), was die Ursache vieler seltsamer Fehler ist.
Code-Demo: vollständige Implementierung des Zweierkomplements
Beispiel
def to_twos_complement(n, bits=8):
Konvertiere die Ganzzahl n in eine Zweierkomplement-Zeichenkette mit der angegebenen Bitbreite
if n < -2**(bits-1) or n > 2**(bits-1) - 1:
raise ValueError(fWert {n} liegt außerhalb des {bits}-Bit-Zweierkomplement-Bereichs [-{2**(bits-1)}, {2**(bits-1)-1}])
# Modulo-Arithmetik verwenden: das vorzeichenlose Äquivalent einer negativen Zahl n = n + 2^bits
# Bitmaske direkt in Python verwenden
mask = (1 << bits) - 1 # Zum Beispiel 8 Bits: 0xFF = 255
unsigned_val = n & mask
return format(unsigned_val, f'0{bits}b')
def from_twos_complement(bin_str):
"""Konvertiert einen Zweierkomplement-String zurück in eine dezimale Ganzzahl."""
bits = len(bin_str)
unsigned_val = int(bin_str, 2)
# Wenn das höchstwertige Bit 1 ist (negativ), subtrahiere 2^bits
if bin_str[0] == '1':
return unsigned_val - (1 << bits)
else:
return unsigned_val
def show_conversion_steps(n, bits=8):
"""Zeige die detaillierten Schritte von n zum Zweierkomplement."""
abs_bin = format(abs(n), f'0{bits}b')
print⟦⟧"Wert: {n}"(f"Value: {n}")
print(fSchritt 1: Absolutwert |{n}| = {abs(n)} → Binärdarstellung = {abs_bin})
if n >= 0:
print(f" Schritt 2: Das Zweierkomplement einer positiven Zahl = Vorzeichen-Betrag-Darstellung = {abs_bin}")
else:
# Invertieren
inverted = ''.join('1' if b == '0' else '0' for b in abs_bin)
print(fSchritt 2: Invertieren (Einerkomplement) = {inverted})
# 1 addieren
carry = 1
result_list = list(inverted)
for i in range(bits - 1, -1, -1):
if carry == 0:
break
total = int(result_list[i]) + carry
result_list[i] = str(total % 2)
carry = total // 2
result = ''.join(result_list)
print(fSchritt 3: Einerkomplement + 1 = {result})
comp = to_twos_complement(n, bits)
print(f" → Endgültiges Zweierkomplement: {comp}")
# Überprüfen
recovered = from_twos_complement(comp)
print(fÜberprüfung: Zweierkomplement {comp} → interpretiert als {recovered})
print()
# Test
print("=" * 50)
print("Demo zur Zweierkomplement-Konvertierung (8-Bit-System)")
print("=" * 50)
print()
for val in [5, -5, 0, -1, 127, -128]:
show_conversion_steps(val)
# Zweierkomplement-Addition überprüfen
print("=" * 50)
print("Überprüfung der Zweierkomplement-Addition: Subtraktion wird zur Addition")
print("=" * 50)
def twos_add(a, b, bits=8):
"""Zweierkomplement-Addition (einschließlich Überlaufbehandlung)"""
mask = (1 << bits) - 1
result_unsigned = (a + b) & mask
# Vorzeichenbehaftetes Ergebnis interpretieren
if result_unsigned >= (1 << (bits - 1)):
signed_result = result_unsigned - (1 << bits)
else:
signed_result = result_unsigned
return signed_result
# 5 - 3 = 2
print(f5 + (-3) = {twos_add(5, -3)} (erwartet: 2))
# 127 + 1 = -128 (Überlauf)
print(f127 + 1 = {twos_add(127, 1)} (erwartet: -128, Überlauf aufgetreten))
# -128 - 1 = 127 (Überlauf)
print(f-128 + (-1) = {twos_add(-128, -1)} (erwartet: 127, Überlauf aufgetreten))
# Null addieren
print(f0 + 0 = {twos_add(0, 0)} (erwartet: 0))
# Mit EXAMPLE-Zeichen überprüfen: R=82, U=85 → 82 + (-85) = -3
print(f"\nEXAMPLE-Test:R(82) + (-U(85)) = {twos_add(82, -85)} (erwartet: -3)")
# Veranschaulichung der zyklischen Natur des Zweierkomplements
print("\nZweierkomplement-Ring (vereinfachte 4-Bit-Version, Bereich -8 bis +7):)
print("Wert | Zweierkomplement | Vorzeichenlos")
print("-" * 30)
for n in range(-8, 8):
comp = to_twos_complement(n, 4)
unsig = int(comp, 2)
print(f" {n:3d} | {comp} | {unsig:2d}")