Wie funktioniert der Ganzzahlvergleich intern?

18

ZB wenn man zwei Ganzzahlen wie folgt in einer C-ähnlichen Sprache vergleicht:

if (3 > 2) {
    // do something
}

Wie wird intern beurteilt, ob 3 größer als 2 (wahr) ist oder nicht (falsch)?

Niek
quelle
19
Die aktuelle Antwort ist in Ordnung für den Fall, dass es sich bei dem Vergleich um einen variablen Ausdruck handelt. Es fehlt jedoch der Hinweis, dass viele moderne Compiler Ihren Code prüfen, feststellen, dass der Ausdruck immer wahr ist (aufgrund von Literalen) und den ifgesamten Ausdruck einfach ignorieren , gehe direkt zur Codierung do something.
SJuan76
3
Mögliches Duplikat von Wie funktionieren Computer?
3
@Schneemann Ich bin anderer Meinung. Jede "Wie funktioniert das?" - Programmierabfrage kann auf diese Frage reduziert werden, macht sie jedoch nicht doppelt.
user1643723
1
@ user1643723 Dies ist der Fall, wenn es sich bei der betreffenden Funktion um einen bestimmten Opcode handelt.
chrylis -on strike-
1
@ user1643723 In der Frage wird gefragt, wie ein Computer eine grundlegende Operation ausführt, und in der oberen Antwort werden Logikgatter und Logiktabellen erläutert. Zwei Themen, die ausführlich in der Top-Antwort des betrogenen Ziels behandelt werden, die auch Ihre Frage beantwortet.

Antworten:

61

Den ganzen Weg durch das Kaninchenloch, was? OK, ich werde es versuchen.

Schritt 1. Von C zur Maschinensprache

Der C-Compiler wandelt Ihren Vergleich in Opcodes um, die in der Maschinensprache gespeichert sind . Maschinensprache ist eine Reihe von Zahlen, die die CPU als Anweisungen interpretiert. In diesem Fall gäbe es zwei Opcodes: "subtrahieren mit Übertrag" und "springen, wenn Übertragen". Mit anderen Worten, in einem Befehl wird 2 von 3 subtrahiert, und der nächste Befehl prüft, ob er übergelaufen ist. Davor stehen zwei Anweisungen zum Laden der Nummern 2 und 3 an Stellen, an denen sie verglichen werden können.

MOV AX, 3    ; Store 3 in register AX
MOV BX, 2    ; Store 2 in register BX
SUB AX, BX   ; Subtract BX from AX
JC  Label    ; If the previous operation overflowed, continue processing at memory location "Label"

Jedes der oben genannten hat eine binäre Darstellung; Der Code für SUBist beispielsweise 2Dhex oder 00101101binär.

Schritt 2. Opcodes an ALU

Arithmetic Opcodes wie ADD, SUB, MULund DIVführen Sie grundlegende Integer - Operationen unter Verwendung eines ALU oder Arithmetik - Logik - Einheit eingebaut in die CPU. Zahlen werden durch einige Opcodes in Registern gespeichert ; andere Operationscodes weisen den Chip an, die ALU aufzurufen, um zu berechnen, was zu der Zeit in den Registern gespeichert ist.

Hinweis: An diesem Punkt sind wir weit über alles hinaus, was ein Softwareentwickler befürchten würde, wenn er mit einer 3GL wie C arbeitet.

Schritt 3. Die ALU, der Halbaddierer und der Volladdierer

Wussten Sie, dass alle Ihnen bekannten mathematischen Operationen auf eine Reihe von NOR-Operationen reduziert werden können ? Und genau so funktioniert die ALU.

Die ALU kann nur mit Binärzahlen arbeiten und kann nur logische Operationen wie OR, NOT, AND und XOR ausführen. Die Implementierung der binären Addition und Subtraktion erfolgt mit einer Reihe von logischen Operationen, die auf eine bestimmte Weise in einem als Addierer bekannten Subsystem angeordnet sind . Diese Subsysteme bestehen aus einem Netzwerk von "Halbaddierern", die mit zwei Bits arbeiten und deren Einzelbitsumme und ein Einzelbit-Übertragsflag bestimmen. Indem diese verkettet werden, kann die ALU Operationen an Zahlen mit 8, 16, 32 usw. Bits ausführen.

Half-Adder

Was ist mit Subtraktion? Subtraktion ist nur eine andere Form der Addition:

A - B = A + (-B)

Die ALU berechnet, -Bindem sie das Zweierkomplement von B. Sobald der Wert in einen negativen Wert umgewandelt wurde, führt das Senden des Werts an den Addierer zu einer Subtraktionsoperation.

Schritt 4: Der letzte Schritt: On-Chip-Transistoren

Die Operationen der Addierer werden unter Verwendung einer Kombination elektrischer Komponenten implementiert, die zusammenwirken, um "Logikgatter" zu erzeugen, wie sie beispielsweise in Transitor-Transistor-Logik oder TTL oder in CMOS zu finden sind . Klicken Sie hier, um einige Beispiele zu sehen, wie diese verkabelt sind.

Auf einem Chip sind diese "Schaltkreise" natürlich in Millionen winziger Stückchen leitenden und nichtleitenden Materials implementiert, aber das Prinzip ist dasselbe, als wären sie Bauteile in voller Größe auf einem Steckbrett. Sehen Sie sich dieses Video an, das Ihnen alle Transistoren eines Mikrochips durch die Linse eines elektronischen Mikroskops zeigt.

Einige zusätzliche Hinweise:

  1. Der von Ihnen geschriebene Code würde tatsächlich vom Compiler vorberechnet und nicht zur Laufzeit ausgeführt, da er ausschließlich aus Konstanten besteht.

  2. Einige Compiler kompilieren nicht mit Maschinencode, sondern führen eine weitere Ebene ein, z. B. Java-Bytecode oder .NET-Zwischensprache. Aber am Ende wird alles über die Maschinensprache ausgeführt.

  3. Einige mathematische Operationen werden nicht berechnet. Sie werden in massiven Tabellen auf einer Arithmetik-Coprozessor-Einheit nachgeschlagen oder enthalten eine Kombination aus Nachschlagen und Berechnen oder Interpolieren. Ein Beispiel wäre die Funktion zur Berechnung einer Quadratwurzel . Moderne PC-CPUs verfügen jeweils über eine in jeden CPU-Kern integrierte Gleitkomma-Coprozessoreinheit.

John Wu
quelle
3
FWIW: Beziehen auf TTL kann verwirrend sein, da praktisch keine modernen Prozessoren TTL-Signale verwenden, die meisten verwenden CMOS-FETs und niedrigere Spannungen anstelle von 5-V-BJTs.
Whatsisname
2
CMOS wäre definitiv eine bessere Referenz als TTL, wie @whatsisname andeutet, da es nicht nur genauer ist als das, was in modernen Prozessoren vor sich geht, sondern auch konzeptionell viel einfacher ist.
Jules
3
@JackAidley das ist, was dieser Teil bedeutet: "Mit anderen Worten, 2 wird in einem Befehl von 3 subtrahiert, und der nächste Befehl prüft, ob er übergelaufen ist."
KutuluMike
1
Nitpicking: Ich nehme an, CMPwürde nicht verwendet werden SUB- aber SUBandererseits ist das mehr oder weniger ein " wo das Ergebnis ignoriert und nur die Flags gesetzt werden"
Hagen von Eitzen
5
Ihre Definitionen von Halb- und Volladdierer sind falsch. Ein Halbaddierer nimmt zwei 1-Bit-Eingänge und gibt die Summe und den Übertrag zurück. Ein Volladdierer benötigt einen zusätzlichen Übertragseingang, ist aber immer noch nur ein einziges Bit. Es gibt viele Möglichkeiten, einen N-Bit-Addierer zu erzeugen, wobei der einfachste ein Ripple-Carry-Addierer ist, der nur eine Kette von N Volladdierern ist. In der Praxis hat dies für größere Ns eine ziemlich schlechte Verzögerung, so dass komplexere Designs in modernen CPU-Designs verwendet werden.
Voo