Hi,
(abschicken sollte man das dann auch noch, Christoph ;-)
Naja, die Frage ist wohl eher, was du als »Anfänger« definierst.
Jeden, der dem Woertchem "noch" besondere Betonung gibt.
Aber ich gebe ja zu, auf dem Gebiet der verschiedenen Bit-Operationen (Shifting, etc.) kenne ich mich (noch) kaum aus und sehe bisher noch nicht wirklich einen Sinn darin, was ich aber zu ändern gedenke.
Man sollte es verstehen koennen. Sinn in der Anwendung macht das aber heutzutage eigentlich nur noch im hardwarenahem Bereich oder beim Numbercrunching.
In der letzten Zeit versuche ich zum Beispiel die verschiedenen Hash-Algorithmen zu verstehen und diese selber zu implementieren.
Die Algorithmen zu implementieren ist meist leicht, sie zu verstehen erfordert aber hoehere Mathematik und da fragst Du besser jemanden, der sich damit auskennt ;-)
Aber ich nehme mal an, das Du unter "Verstehen" hier nur die Implementation meinst.
Dabei wird ja beinahe ausschließlich mit den einzelnen Bits gearbeitet, aber insbesondere das Verständnis »warum so, und nichts anders« bereitet mir im Moment noch einige Probleme.
Prinzipiell weil es einfacher in Hardware zu implementieren ist. Deshalb sind solche Implementationen bei mancher Architektur auch schneller.
Diese Reihenfolge ist nach Moeglichkeit einzuhalten, Ausnahmen duerfen nicht zur Regel werden.
Right- und Leftshift habe ich mittlerweile glaube ich verstanden: Ein Rightshift um n Bits ist ja nichts anderes als eine Division durch 2^n, weil die einzelnen Bits nach Rechts verschoben werden. Ein Leftshift ist dementsprechend eine Multiplikation mit 2^n.
Beim Bitverschieben ist auch noch zusaetzlich bei einigen Architekturen das Vorzeichen und die "Fuellung" zu beachten.
Fuellung ist inbesondere beim Rightshift wichtig, deshalb gibt es in einigen Sprachen auch noch zusaetzlich die Moeglichkeit des Rightshift mit "Nullen links auffuellen" mitunter dargestellt als '>>>'. Mitunter ist das Shifting auch zirkular: was an einer Seite rausfaellt wird an der anderen Seite wieder angefuegt.
Es sind beim -- falsch: natuerlich _vor_ dem Einsatz die Implementation des Shiftings in Sprache und Architektur zu pruefen. Aufgrund dieser Architekturabhaengigkeit ist Bitjuggling auch alles andere als portabel.
Was bringt mir das nun aber genau, d.h. wann schreibe ich
int result, i
i = 8
result = i << 2 // result ist nun 32
Wenn Du den Code in Hardware implementieren moechtest, direkt auf der bekannten(!) Hardware (Kernel, Graphikengine u.ae.) oder tatsaechlich auf Bitebene arbeitest. Mitunter kann der Geschwindigkeitsvorteil auch anderweitig Vorteil bringen und in manchen Faellen ist es auch schlicht einfacher lesbar, da die Shiftingsymbolik dem Leser direkt klarmacht, das da Bitjuggling passiert.
> und wann
>
> ~~~c
> int result, i
> i = 8
> result = 8 * pow(2, 2)
>
»
Wenn es portabel sein soll.
Das »Year & 3« entspricht wohl dem »Year % 4«, wobei sich die 3 aus folgenden Überlegungen herleitet:
[latex]
3_{(10)} = 1 * 2 + 1 * 1 = 11_{(2)}
[/latex]
(Was denn, LaTeX kaputtgespielt, Christian? ;-)
Eine Zahl im Dualsystem ist genau dann durch 4 teilbar, wenn die letzten zwei Ziffern eine Null sind.
Ganz genau: binaer 100 ist dezimal 4.
Insofern sollte bei »Year % 3« immer 0 herauskommen weil eine »&«-Verknüpfung der Bits dann z.B. so aussieht:
[latex]
1101_{(2)} & 0011_{(2)} = 0001_{(2)} \not= 0 \rightarrow 1101_{(2)} = 13_{(10)} ist nicht durch 4 teilbar
[/latex]
Sind meine Gedankengänge soweit richtig?
Ja.
Problem hier: es wird vorausgesetzt, das alle anderen Bits der binaeren Darstellung der dezimalen Zahl '3' auf Null gesetzt sind, das ist nicht ueberall der Fall. Ist hier egal, ich wollt's nur noch mal erwaehnt wissen.
Ab hier steige ich dann aus, kannst du das nachfolgende bitte nochmal genauer (ohne eventuelle Spielereien, die das ganze »feiner«, aber auch schwieriger machen) erklären? D.h. was bezweckst du mit untenstehenden Bit-Shifting?
Steht doch daneben?
Aber gut, kein Problem.
Die Loesung ist normalerweise fuer Hardware gedacht, als Approximationshilfe fuer Flieskommazahlen und hat da auch einen Namen: "Reziproke Multiplikation". Beim Namen bin ich mir allerdings nicht so sicher. Ausgangspunkt ist dabei, das sich alle Multiplikationen mit SHIFT und ADD ausfueheren lassen; zwei Funktionen, die sich leicht in Hardware nachbilden lassen.
result = number << 1; // number * 2
result = (number << 1) + number; // number * 3
result = (number << 2) + number; // number * 5
[...]
result = ((number << 2) + number) << 1; // number * 10
[...]
Die Multiplikationen koennen ja auch als reine Additionen dargestellt werden:
a\*3 = a+a+a
Wir muessen also nur den Multiplikator faktorieren (eine gerade Zahl ist ideal also bei Bedarf vor dem Faktorieren 1 abziehen). Der Faktor '2' kann per SHIFT direkt implementiert werden, der Rest wird addiert.
fakt(2) = 2
fakt(3) = 3 = fakt(2) + 1 = 2 + 1
fakt(5) = 5 = fakt(4) + 1 = 2 \* 2 + 1
fakt(10) = 2 \* 5 = 2 \* fakt(5) = 2 \* (fakt(4) + 1) = 2 \* (2 \* 2 + 1)
Wie man sieht: die Klammern sind hier entscheidend ;-)
> > (Ja, ich weiss auch das 10 keine Primzahl ist ;-)
Das war eigentlich als kleiner Tip gedacht, aber war wohl doch zu versteckt.
so short
Christoph Zurnieden