Zur Haupt­na­vi­ga­ti­on sprin­gen [Alt]+[0] Zum Sei­ten­in­halt sprin­gen [Alt]+[1]

Ver­knüp­fung bi­nä­rer Da­ten­wor­te

Lo­gi­sche Ver­knüp­fun­gen

Wir wol­len uns nun den Ver­knüp­fun­gen bi­nä­rer Daten zu­wen­den. Die lo­gi­schen Ver­knüp­fun­gen gan­zer Da­ten­wor­te stel­len dabei kein gro­ßes Pro­blem dar: Jedes der ein­an­der ent­spre­chen­den Bits der zwei (gleich­gro­ßen!) Da­ten­wor­te wird ein­fach durch ein pas­sen­des Gat­ter mit­ein­an­der ver­knüpft.

Ad­di­ti­on bi­nä­rer Zah­len

Der Al­go­rith­mus der Ad­di­ti­on von Bi­n­är­zah­len ist durch das Ver­fah­ren der schrift­li­chen Ad­di­ti­on ge­ge­ben: wir müs­sen also den Al­go­rith­mus der schrift­li­chen Ad­di­ti­on „in Elek­tro­nik“ über­set­zen. Be­trach­ten wir zu­nächst ein Re­chen­bei­spiel mit zwei 4-stel­li­gen Da­ten­wor­ten (wobei die nie­der­wer­tigs­te Stel­le (also das „LSB“) ganz rechts steht):

0 1 1 0
+ 0 1 0 1
1 0 1 1

Wel­che ele­men­ta­ren Auf­ga­ben müs­sen hier­für ge­löst wer­den? Fol­gen­de Ad­di­tio­nen kön­nen an der nie­der­wer­tigs­ten Stel­le auf­tre­ten:

0 + 0 = 0

0 + 1 = 1

1 + 0 = 1

1 + 1 = 10

Die ers­ten 3 Zei­len stel­len kein Pro­blem dar: so­lan­ge das Er­geb­nis der Ele­men­tar-Ad­di­ti­on ein­stel­lig ist, ist die Welt in Ord­nung. Schwie­ri­ger wird es in der vier­ten Zeile: dort liegt ein zwei­stel­li­ges Er­geb­nis vor, was einen Über­trag 1 in die nächst-hö­her­wer­ti­ge Stel­le zur Folge hat. Bei den ers­ten drei Rech­nun­gen hat der Über­trag hin­ge­gen stets den Wert 0. Zur Be­wäl­ti­gung der Ad­di­ti­on zwei­er ein­stel­li­ger Bi­n­är­zah­len brau­chen wir also eine Schal­tung mit 2 Ein­gän­gen A und B sowie zwei Aus­gän­gen S (für den Stel­len­wert) und C (Carry, eng­lisch für den Über­trag). Diese Schal­tung muss die fol­gen­de Wahr­heits­wert-Ta­bel­le haben:

a c cout s
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 0

Diese Schal­tung nennt man einen Halb­ad­die­rer und stellt sie durch das fol­gen­de Sym­bol dar:

Symbol Halbaddierer (eigenes Werk)

Sym­bol Halb­ad­die­rer

Das fol­gen­de Bild zeigt eine Im­ple­men­tie­rung eines Halb­ad­die­rers. Es gibt al­ler­dings auch Im­ple­men­tie­run­gen, die mit we­ni­ger Tran­sis­to­ren aus­kom­men:

Halbaddierer (eigenes Werk)

Halb­ad­die­rer

Warum ist dies nur ein „Halb“-Ad­die­rer? Die zu­rück­hal­ten­de Be­zeich­nung ist dar­auf zu­rück­zu­füh­ren, dass die Schal­tung zwar den Über­trag auf der Aus­gangs­sei­te kor­rekt pro­du­zie­ren kann, aber kei­nen Ein­gang für einen even­tu­ell von einer nie­der­wer­ti­ge­ren Stel­le ge­lie­fer­ten Über­trag hat. Stu­diert man noch­mals den Al­go­rith­mus der schrift­li­chen Ad­di­ti­on, dann fällt auf, dass für jede Stel­le (außer der nie­der­wer­tigs­ten) wegen des mög­li­chen Über­trags der vor­he­ri­gen Stel­le die Ad­di­ti­on von ei­gent­lich 3(!) ein­stel­li­gen Bi­när­wer­ten er­mög­licht wer­den muss.

Ana­log zum obi­gen Vor­ge­hen könn­te man nun die Wahr­heits­wert-Ta­bel­le eines Vol­l­ad­die­rers mit 3 Ein­gän­gen auf­stel­len und ver­su­chen, eine ent­spre­chen­de Schal­tung zu ent­wer­fen. Dies ist eine loh­nen­de Haus­auf­ga­be für am­bi­tio­nier­te Schü­ler. Al­ler­dings ist so­wohl die er­folg­rei­che Be­ar­bei­tung der Auf­ga­be als auch eine even­tu­el­le Über­prü­fung und Kor­rek­tur der er­ar­bei­te­ten Lö­sun­gen recht müh­sam.

Der An­satz, aus der Wahr­heits­wert­ta­bel­le die Schal­tung zu ent­wi­ckeln, er­in­nert an die „Brute-Force-Me­tho­de“. Ge­schick­ter ist hier die An­wen­dung eines „Di­vi­de-and-Con­quer“-An­sat­zes: man zer­le­ge das Pro­blem in klei­ne­re Ein­zel­pro­ble­me, löse diese und ver­su­che dann, die Lö­sung des ur­sprüng­li­chen Pro­blems aus den Ein­zel­lö­sun­gen zu­sam­men­zu­set­zen. Zu­min­dest die Zer­le­gung geht im vor­lie­gen­den Fall sehr ein­fach: statt eine Ad­di­ti­on drei­er Bits „in einem Rutsch“ zu be­trach­ten, neh­men wir zu­erst mal zwei der Bits und ad­die­ren diese. Dann ad­die­ren wir zum Zwi­schen­er­geb­nis das drit­te Ein­gangs­bit. Ei­gent­lich müs­sen wir also nur zwei Halb­ad­die­rer hin­ter ein­an­der schal­ten:

Addition von drei Bits (eigenes Werk)

Ad­di­ti­on von drei Bits

Der Aus­gang S ent­hält of­fen­sicht­lich den Stel­len­wert des Er­geb­nis­ses. Aber wel­cher der bei­den Über­trä­ge Ü1 und Ü2 ist der rich­ti­ge? Nun, da ein Über­trag bei der ers­ten oder auch bei der zwei­ten Ad­di­ti­on auf­tre­ten könn­te, müs­sen ir­gend­wie beide Über­trä­ge be­rück­sich­tigt wer­den.

Die kom­plet­te Vol­l­ad­die­rer-Schal­tung wird mit dem fol­gen­den Sym­bol ab­ge­kürzt:

Volladdierer (eigenes Werk)

Vol­l­ad­die­rer

Eine Schal­tung zur Ad­di­ti­on zwei­er 4-Bit-Bi­n­är­zah­len könn­te dann ent­spre­chend so aus­se­hen:

Carry-Ripple-Addierer (eigenes Werk)

Carry-Ripp­le-Ad­die­rer

Ach­tung! Die Ad­di­ti­on zwei­er 4-Bit-Zah­len kann eine 5-Bit-Zahl er­ge­ben. Für das Ab­spei­chern der voll­stän­di­gen Summe ist also eine grö­ße­re Wort­brei­te nötig als für die Sum­man­den! Die­ses Pro­blem taucht bei sämt­li­chen nu­me­ri­schen Da­ten­ty­pen auf und muss ent­spre­chend in allen Be­rech­nungs­al­go­rith­men be­rück­sich­tigt wer­den.

Sub­trak­ti­on bi­nä­rer Zah­len

Bis­her haben wir die ver­wen­de­ten 4-Bit-Worte bei der In­ter­pre­ta­ti­on als Zah­len stets als na­tür­li­che Zah­len an­ge­se­hen. Gemäß der fol­gen­den Ta­bel­le lässt sich also mit einem sol­chen 4-Bit-Wort ein Zah­len­be­reich von 0..15 be­schrei­ben:

Bits Zahl
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7

Bits Zahl
1000 8
1001 9
1010 10
1011 11
1100 12
1101 13
1110 14
1111 15

Ad­diert man zu 1111b = 15 nun noch­mals eine 1, dann er­hält man 10000b = 16, was aber 5 Bits zur Spei­che­rung braucht und daher nicht mehr in ein 4-Bit-Wort passt. Schreibt man die­ses Er­geb­nis dann eben „so­weit es geht“ stel­len­ge­recht in einen 4-Bit-Spei­cher, dann er­hält man als Re­sul­tat die­ser Ad­di­ti­on das Bit­mus­ter „0000“. Mit­hin er­scheint der Zah­len­raum zy­klisch ge­schlos­sen:

zy­kli­sche An­ord­nung

In einer sol­chen zy­kli­schen An­ord­nung ist die In­ter­pre­ta­ti­on der Bit­mus­ter als rein po­si­ti­ve ganze Zah­len nicht mehr zwin­gend. Man könn­te auch eine Hälf­te der Zah­len als po­si­ti­ve und die an­de­re Hälf­te als ne­ga­ti­ve Zah­len an­se­hen. Da in der Menge der gan­zen Zah­len die 0 den Vor­gän­ger -1 hat, müss­te die­ser Zahl das Bit­mus­ter „1111“ ent­spre­chen, der -2 das Mus­ter „1110“. Üb­li­cher­wei­se wird das höchst­wer­ti­ge Bit als Vor­zei­chen-In­di­ka­tor ge­nom­men: alle Bit­mus­ter der Form „1xxx“ wer­den als ne­ga­tiv in­ter­pre­tiert, alle der Form „0xxx“ als po­si­tiv. Die sich damit er­ge­ben­de Zu­ord­nung ist im fol­gen­den Dia­gramm durch den zu­sätz­li­chen in­ne­ren Zah­len­kreis dar­ge­stellt:

Zweier-Komplement-Darstellung

Zwei­er-Kom­ple­ment-Dar­stel­lung

So lässt sich der Zah­len­raum von -8 bis +7 mit 4-Bit-Wor­ten dar­stel­len. Man nennt die hier ge­wähl­te Dar­stel­lung der ne­ga­ti­ven Zah­len die Zwei­er-Kom­ple­ment-Dar­stel­lung.

Was uns noch fehlt, ist ein Al­go­rith­mus, mit dem wir aus dem Bit­mus­ter für eine Zahl a > 0 das Bit­mus­ter der Zahl (‑a) er­hal­ten kön­nen. Schau­en wir uns dazu mal ein Bei­spiel an:

Die Zahl 3 hat das Bit­mus­ter „0011“. Wenn wir nun die­ses Mus­ter bit­wei­se in­ver­tie­ren (also jede 0 durch eine 1 er­set­zen und jede 1 durch eine 0), dann er­hal­ten wir das Mus­ter „1100“. Ein Blick in die obige Ta­bel­le zeigt, dass wir dicht neben dem rich­ti­gen Er­geb­nis lan­den, näm­lich bei (-4). Wenn wir nun zu die­ser Zahl noch eine 1 ad­die­ren, dann er­hal­ten wir das kor­rek­te Bit­mus­ter „1101“ für (-3). All­ge­mein gilt der fol­gen­de Al­go­rith­mus zur Bil­dung des Zwei­er­kom­ple­ments einer Zahl a:

Man in­ver­tie­re das Bit­mus­ter von a und ad­die­re zum Er­geb­nis eine 1.

Wenn wir das bit­wei­se In­ver­se einer Zahl a mit a be­zeich­nen und das Zwei­er-Kom­ple­ment von a mit a*, dann lässt sich die obige Regel als For­mel schrei­ben:

a* = a + 1

Um den Nut­zen der Zwei­er­kom­ple­ment-Dar­stel­lung für ne­ga­ti­ve Zah­len ein­zu­se­hen, klä­ren wir zu­nächst, wie man die Bil­dung des Zwei­er­kom­ple­ments einer Zahl a ma­the­ma­tisch be­schrei­ben kann. Liegt a in einer Dar­stel­lung mit N Bits vor, dann kann man das bit­wei­se In­ver­se von a, also a, er­hal­ten, indem man a von einer Bi­n­är­zahl ab­zieht, die aus genau N Ein­sen be­steht. Im obi­gen kon­kre­ten Bei­spiel wäre also zu rech­nen: „1111“ - „0011“ = „1100“, wobei die Dif­fe­renz in Zah­len ge­schrie­ben be­deu­tet: (24 – 1) – 3. Die Zwei­er­kom­ple­ment-Dar­stel­lung er­hält man nun, indem man zu die­sem Term noch eine 1 ad­diert: (24 – 1) – 3 + 1 = 24 – 3. All­ge­mein er­gibt sich für das Zwei­er-Kom­ple­ment a* einer po­si­ti­ven Zahl a in N-stel­li­ger Bi­när­dar­stel­lung der Term

a* = 2N – a.

Damit lässt sich eine Sub­trak­ti­on zwei­er po­si­ti­ver Zah­len stets fol­gen­der­ma­ßen rea­li­sie­ren:

b – a = b – a + 2N – 2N = b + 2N – a – 2N = b + (2N – a) – 2N = b + a* – 2N

Zur Aus­füh­rung der Sub­trak­ti­on b - a kann also ein nor­ma­les Ad­dier­werk ver­wen­det wer­den, wenn man an des­sen Ein­gän­ge den Mi­nu­en­den b und die Zwei­er­kom­ple­ment-Dar­stel­lung a* des Sub­tra­hen­den an­legt und vom Er­geb­nis schließ­lich noch 2N ab­zieht. Die Sub­trak­ti­on von 2N schließ­lich kön­nen wir ein­fach rea­li­sie­ren, indem wir ein even­tu­ell ge­setz­tes Über­trags-Bit in der höchst­wer­ti­gen Stel­le des Er­geb­nis­ses igno­rie­ren: da die­ses zu­sätz­li­che Bit oh­ne­hin beim Ab­spei­chern in einem N-Bit-Da­ten­wort kei­nen Platz hat, lässt man es ein­fach un­be­rück­sich­tigt.

Das Pro­blem der Sub­trak­ti­on wird also voll­stän­dig durch die Zwei­er-Kom­ple­ment-Ko­die­rung der ne­ga­ti­ven gan­zen Zah­len ge­löst, und zwar gemäß der Um­for­mung:

b – a = b + (-a)

die wir ja aus der Ma­the­ma­tik ken­nen. Für uns sieht diese Glei­chung nun (bei Be­schrän­kung auf N Bi­närs­tel­len!) so aus:

b – a = b + a*.

Pro­ble­me beim Rech­nen mit be­schränk­ter Stel­len­zahl

Damit sind noch nicht alle Pro­ble­me der bi­nä­ren Ad­di­ti­on bzw. Sub­trak­ti­on ge­löst. Das be­rech­ne­te Er­geb­nis EB stimmt näm­lich nur dann mit dem kor­rek­ten Er­geb­nis E über­ein, wenn letz­te­res(!) in­ner­halb des zu­läs­si­gen Zahl­be­reichs (hier: [‑8;+7]) liegt. Das be­rech­ne­te Er­geb­nis EB liegt schein­bar immer in die­sem Zahl­be­reich, was schon durch die Hard­ware des Ad­die­rers ge­si­chert ist! Für un­se­re 4-Bit-Zah­len ist z.B. schon die Ad­di­ti­on 6 + 3 eine un­über­wind­li­che Hürde, denn sie lie­fert Ü = 0 und das Bit­mus­ter S = 1001, und damit EB = (-7)! Solch ein „Über­lauf“ soll­te durch ge­eig­ne­te Maß­nah­men er­kannt wer­den, so dass dann die Soft­ware zu­min­dest mel­den kann, dass die Be­rech­nung fehl­ge­schla­gen ist. Al­ler­dings ist die Er­ken­nung nicht ein­fach zu im­ple­men­tie­ren. Das hier an­ge­ge­be­ne Bei­spiel lehrt schon, dass die Über­wa­chung des Ü-Aus­gangs der Re­chen­schal­tung kei­nes­falls ge­nügt, und dass „Über­trag“ und „Über­lauf“ zwei total ver­schie­de­ne Dinge sind!

Das Über­lauf­pro­blem ist ein grund­sätz­li­ches Pro­blem der Com­pu­te­ra­rith­me­tik, weil alle nu­me­ri­schen Da­ten­ty­pen fes­ter Größe eben stets nur end­lich viele ver­schie­de­ne Werte an­neh­men kön­nen. Mit­hin gibt es stets eine größ­te und eine kleins­te dar­stell­ba­re Zahl. Über­schrei­ten die kor­rek­ten Er­geb­nis­se einer Auf­ga­be die da­durch ge­setz­ten Gren­zen, kön­nen die Be­rech­nungs­al­go­rith­men sinn­lo­se Werte lie­fern. Um dies zu ver­mei­den, kann man sich nu­me­ri­scher Da­ten­ty­pen be­die­nen, die nicht mit einer fes­ten Stel­len­zahl ar­bei­ten. So gibt es z.B. in JAVA zur Dar­stel­lung gro­ßer Zah­len die dy­na­mi­schen Da­ten­ty­pen Bi­g­In­te­ger und Big­De­zi­mal, wel­che das Rech­nen mit be­lie­big vie­len Stel­len er­lau­ben. Der Um­gang damit ist aber nicht ganz ein­fach, und die Per­for­mance sol­cher Pro­gram­me kann pro­ble­ma­tisch sein. So wird man sich in den meis­ten Fäl­len doch der end­li­chen Stan­dard-Da­ten­ty­pen be­die­nen.

Viele Com­pi­ler ver­zich­ten aus Per­for­mance-Grün­den zu­min­dest bei der In­te­ger-Arith­me­tik auf jeg­li­che Über­lauf­er­ken­nung (z.B. die Pas­cal-Com­pi­ler der Del­phi-Serie). So bleibt es dem Pro­gram­mie­rer der Soft­ware an­heim­ge­stellt, seine Pro­gram­me selbst gegen Über­läu­fe zu si­chern. Lässt man dabei nicht die ge­bo­te­ne Sorg­falt wal­ten, kann das durch­aus schlim­me Fol­gen haben. Ein Über­lauf war z.B. 1996 der Grund dafür, dass die Aria­ne5-Ra­ke­te auf ihrem Jung­fern­flug ab­stürz­te. Im Na­vi­ga­ti­ons­sys­tem der Ra­ke­te wurde vor dem Start eine 64-Bit-Float-Zahl ge­run­det und der ge­run­de­te Wert in eine 16-Bit-In­te­ger-Zahl ge­schrie­ben. Lei­der ergab sich beim Run­den ein Wert, der mehr als 16 Bits groß war, wes­halb ei­ni­ge der hö­her­wer­ti­gen Bits beim Spei­chern ver­lo­ren gin­gen. Der fal­sche ge­spei­cher­te Wert führ­te dann bei wei­te­ren Be­rech­nun­gen zum Ab­sturz des Na­vi­ga­ti­ons­sys­tems der Ra­ke­te. Der damit ori­en­tie­rungs­los ge­wor­de­ne Haupt­com­pu­ter der Ra­ke­te än­der­te die Flug­bahn da­nach ei­gen­mäch­tig so stark ab, dass die Bo­den­sta­ti­on die Selbst­zer­stö­rung der Ra­ke­te ein­lei­ten muss­te.

 

 

Hin­ter­grund­in­for­ma­tio­nen: Her­un­ter­la­den [odt][4 MB]

 

Wei­ter zu Re­gis­ter­ma­schi­ne