Tracks
/
Julia
Julia
/
Übungen
/
Geheimnisse
Geheimnisse

Geheimnisse

Lernübung

Einführung

Binärziffern entsprechen letztlich direkt den Transistoren in deiner CPU oder deinem RAM und ob jeder von ihnen „an" oder „aus" ist.

Low-Level-Manipulation, informell „Bit-Twiddling" genannt, ist in Systemsprachen besonders wichtig.

Hochsprachen wie Julia abstrahieren meist den größten Teil dieser Details. Allerdings steht in der Basissprache eine ganze Reihe von Bit-Operationen zur Verfügung.

Hinweis: Um im REPL eine für Menschen lesbare binäre Ausgabe zu sehen, müssen fast alle Beispiele unten in eine bitstring()-Funktion eingepackt werden. Das lenkt optisch ab, deshalb wurden die meisten Vorkommen dieser Funktion herausgenommen.

Bitverschiebungen

Ganzzahltypen, ob vorzeichenbehaftet oder vorzeichenlos, lassen sich als eine Folge von 1en und 0en darstellen.

julia> bitstring(UInt8(5))
"00000101"

Bitverschiebungen verschieben einfach alles um eine bestimmte Anzahl von Positionen nach links oder rechts. Bei UInt-Typen fallen an einem Ende einige Bits heraus und das andere Ende wird mit Nullen aufgefüllt:

julia> ux::UInt8 = 5
5

julia> bitstring(ux)
"00000101"

julia> ux << 2 # left by 2
"00010100"

julia> ux >> 1 # right by 1
"00000010"

Jede Verschiebung nach links verdoppelt den Wert und jede Verschiebung nach rechts halbiert ihn (vorbehaltlich einer Trunkierung). In der Dezimaldarstellung ist das deutlicher:

julia> 3 << 2
12

julia> 24 >> 3
3

Solche Bitverschiebungen sind viel schneller als „richtige" Arithmetik, weshalb die Technik beim Low-Level-Programmieren sehr beliebt ist.

Bei vorzeichenbehafteten Ganzzahlen müssen wir etwas vorsichtiger sein.

Verschiebungen nach links sind relativ einfach:

julia> sx = Int8(5)
5

julia> sx # positive integer
"00000101"

julia> sx << 2
"00010100"

julia> -sx # negative integer
"11111011"

julia> -sx << 2
"11101100"

Das Verschieben positiver vorzeichenbehafteter Ganzzahlen nach links ist also dasselbe wie bei vorzeichenlosen Ganzzahlen.

Negative Werte werden in der Zweierkomplement-Form gespeichert, was bedeutet, dass das am weitesten links stehende Bit eine 1 ist. Für eine Verschiebung nach links ist das kein Problem, aber wie füllen wir beim Verschieben nach rechts die am weitesten links stehenden Bits auf?

julia> sx >> 2 # simple for positive values!
"00000001"

julia> -sx # negative integer
"11111011"

julia> -sx >> 2 # pad with repeated sign bit
"11111110"

julia> -sx >>> 2 # pad with 0
"00111110"

Der Operator >> führt eine arithmetische Verschiebung durch und erhält dabei das Vorzeichenbit.

Der Operator >>> führt eine logische Verschiebung durch und füllt mit Nullen auf, als wäre die Zahl vorzeichenlos.

Bitweise Logik

In einem früheren Konzept haben wir gesehen, dass die Operatoren && (und), || (oder) und ! (nicht) mit booleschen Werten verwendet werden.

Es gibt entsprechende Operatoren & (bitweises Und), | (bitweises Oder) und ~ (eine Tilde, bitweises Nicht), um die Bits zweier Ganzzahlen zu vergleichen.

julia> 0b1011 & 0b0010 # bit is 1 in both numbers
"00000010"

julia> 0b1011 | 0b0010 # bit is 1 in at least one number
"00001011"

julia> ~0b1011 # flip all bits
"11110100"

julia> xor(0b1011, 0b0010) # bit is 1 in exactly one number, not both
"00001001"

Hier ist xor() das exklusive Oder, verwendet als Funktion.

Anleitung

Dein Freund hat dir gerade eine Nachricht mit einem wichtigen Geheimnis geschickt. Weil er es anderen nicht leicht machen wollte, sie zu lesen, wurde die Nachricht durch eine Reihe von Bitmanipulationen verschlüsselt. Du musst die Funktionen schreiben, die beim Entschlüsseln der Nachricht helfen.

1. Verschiebe die Bits zurück

Der erste Schritt beim Entschlüsseln der Nachricht besteht darin, die Verschiebung aus dem Verschlüsselungsprozess rückgängig zu machen, indem du die Bits wieder nach rechts verschiebst. In den weiteren Schritten der Entschlüsselung wird davon ausgegangen, dass von der linken Seite 0en eingefügt werden.

Implementiere die Funktion shift_back(), die einen Wert und die Anzahl der Stellen zum Verschieben entgegennimmt und die Verschiebung durchführt.

shift_back(0b1001, 2) # => 0b0010

2. Setze einige Bits

Als Nächstes gibt es einige Bits, die auf 1 gesetzt werden müssen.

Implementiere die Funktion set_bits(), die einen Wert und eine Maske entgegennimmt und das Ergebnis davon zurückgibt, dass die Bits in value auf 1 gesetzt werden. Ein Bit aus value sollte auf 1 gesetzt werden, wenn das Bit in der mask ebenfalls 1 ist. Alle anderen Bits bleiben unverändert.

set_bits(0b0110, 0b0101) # => 0b0111

3. Drehe bestimmte Bits um

Beim Verschlüsseln werden einige Bits umgedreht. Um die Nachricht zu entschlüsseln, müssen sie wieder zurückgedreht werden.

Implementiere die Funktion flip_bits(), die einen Wert und die Maske entgegennimmt. Die Maske gibt an, welche Bits im Wert umgedreht werden. Wenn das Bit in mask 1 ist, wird das Bit im value umgedreht. Alle anderen Bits bleiben unverändert.

flip_bits(0b1100, 0b0101) # => 0b1001

4. Lösche bestimmte Bits

Zuletzt gibt es außerdem bestimmte Bits, die immer zu 0 entschlüsselt werden.

Implementiere die Funktion clear_bits(), die einen Wert und eine Maske entgegennimmt. Die Bits im value sollten auf 0 gesetzt werden, wenn das Bit in der Maske 1 ist. Alle anderen Bits bleiben unverändert.

clear_bits(0b0110, 0b0101) # => 0b0010
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Julia Exercism

Bereit, mit Geheimnisse zu starten?

Melde dich bei Exercism an, um Julia mit 35 Konzepte128 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.