kind: reference search: Bit, Byte, binär, Binärzahl, hexadezimal, Hex, 0x, 0b, Hexdump, hd, xxd, ASCII, UTF-8, Umlaut, Little Endian, Big Endian, Zweierkomplement, negativ, Überlauf, AND, OR, XOR, Shift, Maske, KiB, MiB

Bits und Bytes

Was in jeder Datei und jedem Speicher steckt, zum Nachschlagen: Zahlen binär und hexadezimal, wie viel in ein Byte passt, wie man einen Hexdump liest, Text als Bytes, Zahlen aus mehreren Bytes, negative Zahlen und die Operationen auf einzelnen Bits, in C, Python, Ruby und JavaScript. Gebraucht wird das in Dateien und Verzeichnisse, GIF, JPEG, Geheime Botschaften in Bildern, Einmalcodes und Vom Byte zum 3D-Labyrinth.

1. Binär, dezimal, hexadezimal

Eine Zahl ist dieselbe, egal wie man sie aufschreibt. Binär hat jede Stelle den doppelten Wert der Stelle rechts von ihr (1, 2, 4, 8, 16 …), hexadezimal den 16-fachen, mit den Ziffern 0–9 und a–f für 10 bis 15:

$$101010_2 = 32 + 8 + 2 = 42$$

$$2a_{16} = 2 \cdot 16 + 10 = 42$$

dezimal binär hex
0 0000 0
1 0001 1
2 0010 2
3 0011 3
4 0100 4
5 0101 5
6 0110 6
7 0111 7
8 1000 8
9 1001 9
10 1010 a
11 1011 b
12 1100 c
13 1101 d
14 1110 e
15 1111 f

Eine Hex-Ziffer steht für genau vier Bits. Deshalb geht das Umrechnen zwischen binär und hexadezimal ohne Rechnen, Ziffer für Ziffer: 0010 1010 ist 2a, 1111 0000 ist f0. Ein Byte sind immer zwei Hex-Ziffern, von 00 bis ff.

C Python Ruby JavaScript
im Code 0x2a; binär erst ab C23 0x2a, 0b101010, mit Unterstrich 0b0010_1010 0x2a, 0b101010, 0b0010_1010 0x2a, 0b101010, 0b0010_1010
als Text binär – format(42, '08b') → '00101010' 42.to_s(2), '%08b' % 42 (42).toString(2)
als Text hex printf("%02x", 42) format(42, '02x'), hex(42) → '0x2a' 42.to_s(16), '%02x' % 42 (42).toString(16)
aus Text strtol("2a", NULL, 16) int('2a', 16), int('101010', 2) '2a'.to_i(16), '101010'.to_i(2) parseInt('2a', 16)

Mit 08b oder rjust(8, '0') füllst du vorn mit Nullen auf, damit ein Byte immer acht Stellen hat.

2. Wie viel passt hinein?

Ein Bit ist 0 oder 1, ein Byte sind 8 Bits. Mit \(n\) Bits gibt es \(2^n\) verschiedene Werte:

Bits Werte ohne Vorzeichen mit Vorzeichen
4 16 0 bis 15 −8 bis 7
8 (ein Byte) 256 0 bis 255 −128 bis 127
16 65 536 0 bis 65 535 −32 768 bis 32 767
32 etwa 4,3 Milliarden 0 bis 4 294 967 295 etwa ±2,1 Milliarden
Schwarzweißporträt von Claude Shannon als junger Mann in Anzug und Krawatte.

Claude Shannon (1916–2001). Wie viele Bits eine Nachricht mindestens braucht, rechnete er 1948 aus und gründete damit die Informationstheorie; das Wort Bit machte er bekannt. Er fuhr Einrad, jonglierte und baute die Maus Theseus, die selbst lernte, durch ein Labyrinth zu finden.

Einen Code mit so wenigen Bits wie möglich suchten er und Robert Fano fast gleichzeitig, jeder für sich. Der Shannon-Fano-Code kam nah heran, aber nicht immer ganz.

Foto: Tekniska museet, CC BY 2.0, zugeschnitten (Wikimedia Commons)

Wie viele Bits braucht man für eine Zahl \(x\)? So viele, dass \(2^n > x\) ist: für 100 sieben, denn \(2^7 = 128\).

Bei Dateigrößen ist kB eigentlich 1000 Bytes und KiB 1024 (\(2^{10}\)), ebenso MB und MiB (\(2^{20}\) = 1 048 576). ls -lh und du -h rechnen mit 1024, schreiben aber nur K und M.

3. Einen Hexdump lesen

hd (»hexdump«) und xxd zeigen die Bytes einer Datei. Eine Datei mit dem Text Grüße, Welt! und einem Zeilenende:

hd gruss.txt
00000000  47 72 c3 bc c3 9f 65 2c  20 57 65 6c 74 21 0a     |Gr....e, Welt!.|
0000000f
  • Vorne steht der Offset: die Position des ersten Bytes dieser Zeile, hexadezimal, ab 0. Die letzte Zeile nennt die Länge, hier 0f = 15 Bytes.
  • In der Mitte stehen bis zu 16 Bytes, mit einer Lücke nach dem achten. Das Byte bei Offset 0x0a ist also das dritte nach der Lücke, hier 65.
  • Rechts stehen dieselben Bytes als Zeichen. Wo ein Byte kein druckbares ASCII-Zeichen ist, steht ein Punkt; ü und ß sind je zwei solche Bytes.
Befehl Was er zeigt
hd datei | less die ganze Datei, seitenweise
hd -n 64 datei nur die ersten 64 Bytes
hd -s 0x0a -n 16 datei 16 Bytes ab Offset 10
xxd datei | head dasselbe in der Form von xxd: 00000000: 4772 c3bc …, je zwei Bytes zusammen, die Zeichen ohne Striche
xxd -b -l 4 datei die ersten 4 Bytes binär

Ein Hexdump interpretiert nichts: Ob zwei Bytes eine Zahl, zwei Zeichen oder ein Stück Bild sind, weiß nur, wer das Format der Datei kennt.

4. Text als Bytes

In ASCII hat jedes Zeichen eine Nummer von 0 bis 127, ein Byte:

Zeichen hex dezimal
Zeilenende (LF, \n) 0a 10 Tab (\t) ist 09
Leerzeichen 20 32
0 bis 9 30 bis 39 48 bis 57 die Ziffer ist das Byte minus 48
A bis Z 41 bis 5a 65 bis 90
a bis z 61 bis 7a 97 bis 122 32 (0x20) mehr als der Großbuchstabe

Alles andere, Umlaute, €, Emojis, schreibt UTF-8 mit zwei bis vier Bytes, die alle größer als 127 sind: ä ist c3 a4, ß ist c3 9f, € ist e2 82 ac. Ein Text aus reinem ASCII ist deshalb auch gültiges UTF-8. Zeichen und Bytes zu zählen, gibt verschiedene Ergebnisse:

Zeichen Bytes
Python len('Grüße') → 5, ord('A') → 65 'Grüße'.encode() → b'Gr\xc3\xbc\xc3\x9fe', 7 Bytes; zurück mit .decode()
Ruby 'Grüße'.length → 5, 'A'.ord → 65 'Grüße'.bytes → [71, 114, 195, 188, 195, 159, 101], bytesize → 7
Terminal wc -m datei wc -c datei

Wer Bytes als Text liest, muss wissen, wie sie kodiert sind. Steht statt Grüße plötzlich Grüße da, wurden UTF-8-Bytes einzeln als Zeichen gelesen.

Ken Thompson 2019: ein Mann mit Brille, weißem Bart und Halbglatze in einem schwarzen T-Shirt.

Ken Thompson (geboren 1943). Du suchst im Terminal mit grep? Das hat er 1973 geschrieben; der Name kommt aus seinem Texteditor, wo der Befehl g/re/p hieß: global, regular expression, print. Wie man aus einem regulären Ausdruck einen Automaten baut, hatte er schon 1968 beschrieben: die Thompson-Konstruktion.

Das Terminal selbst läuft unter Linux, und Linux funktioniert wie Unix, das er ab 1969 mit Dennis Ritchie schrieb. Unten rechts in VS Code steht UTF-8: Die Kodierung hat er 1992 mit Rob Pike auf ein Tischset in einem Diner gezeichnet. Und die Sprache Go hat er mit erfunden.

Foto: A. C. Diller, 2019, CC BY-SA 4.0, zugeschnitten (Wikimedia Commons)

5. Zahlen aus mehreren Bytes

Eine Zahl, die nicht in ein Byte passt, steht in mehreren, und jedes Format legt fest, in welcher Reihenfolge. 0x1234 = 4660 in zwei Bytes:

Reihenfolge im Hexdump wo
Little Endian: das niedrige Byte zuerst 34 12 GIF, BMP, WAV, der Speicher von PCs (x86, ARM)
Big Endian: das hohe Byte zuerst 12 34 JPEG, PNG, Zahlen in Netzwerkprotokollen
zwei Bytes lesen eine Zahl als Bytes
C low | (high << 8), (high << 8) | low x & 0xff, (x >> 8) & 0xff
Python int.from_bytes(data[i:i + 2], 'little'), 'big' x.to_bytes(2, 'little'), 'big'
Ruby data.byteslice(i, 2).unpack1('v'), 'n' für Big Endian [x].pack('v'), [x].pack('n')

Bei Ruby lesen v und V (nach dem alten Rechner VAX) Little Endian, n und N (wie network) Big Endian; die kleinen Buchstaben 16 Bit, die großen 32 Bit. Liest du eine Zahl in der falschen Reihenfolge, wird sie fast immer viel zu groß: 34 12 als Big Endian ist 13 330 statt 4660.

6. Negative Zahlen

Im Zweierkomplement zählt das oberste Bit negativ: Bei einem Byte ist es −128 statt +128. Dieselben acht Bits bedeuten so je nachdem, ob sie mit oder ohne Vorzeichen gelesen werden, etwas anderes:

Bits hex ohne Vorzeichen mit Vorzeichen
0111 1111 7f 127 127
1000 0000 80 128 −128
1111 1011 fb 251 −5
1111 1111 ff 255 −1

Eine Zahl negieren: alle Bits umdrehen und 1 addieren. Aus 5 = 0000 0101 wird 1111 1010, plus 1 1111 1011. Dass dabei −5 herauskommt, sieht man an 251 = 256 − 5: Mit 8 Bits ist −x dasselbe Muster wie 256 − x. Das ist der Grund für dieses Verfahren: Addieren und Subtrahieren funktionieren für beide Lesarten mit denselben Schaltungen.

Wird ein Ergebnis zu groß, läuft es über: Was nicht mehr in die Bits passt, fällt weg. Ein Byte ohne Vorzeichen geht von 255 plus 1 auf 0, eins mit Vorzeichen von 127 plus 1 auf −128.

Was gilt
C feste Größen: uint8_t von 0 bis 255 läuft über (255 + 1 ergibt 0), (int8_t) 200 ist −56; bei int ist ein Überlauf nicht erlaubt, das Programm darf dann alles tun
Python, Ruby ganze Zahlen werden einfach größer und laufen nie über; ein Byte daraus macht x & 0xff (−5 wird 251), zurück int.from_bytes(b, 'big', signed=True) oder in Ruby [b].pack('C').unpack1('c')
JavaScript die Bit-Operationen rechnen mit 32 Bit und Vorzeichen: 1 << 31 ist −2 147 483 648; x >>> 0 liest dieselben Bits ohne Vorzeichen

7. Mit Bits rechnen

Die Operationen arbeiten auf jedem Bit für sich und heißen in C, Python, Ruby und JavaScript gleich. Mit a = 1100 und b = 1010:

Operation Ergebnis ein Bit ist 1, wenn …
a & b (UND) 1000 es in beiden 1 ist
a | b (ODER) 1110 es in mindestens einem 1 ist
a ^ b (XOR, exklusives ODER) 0110 es in genau einem 1 ist; zweimal dasselbe ^ b ergibt wieder a
~a (NICHT) …0011 es in a 0 ist; alle Bits davor werden auch 1, siehe unten
a << 1 11000 schiebt nach links, hinten kommen Nullen dazu: mal 2
a >> 2 0011 schiebt nach rechts, die untersten Bits fallen weg: durch 4, ohne Rest

Die Bits zählt man von rechts, beim untersten mit 0: Bit \(k\) hat den Wert \(2^k\), und 1 << k ist eine Zahl, in der nur dieses Bit gesetzt ist. Eine solche Zahl heißt Maske:

Was Wie
Bit k lesen (0 oder 1) (x >> k) & 1
Bit k setzen, löschen, umdrehen x | (1 << k), x & ~(1 << k), x ^ (1 << k)
die untersten n Bits x & ((1 << n) - 1), für vier Bits x & 0x0f
die obere Hälfte eines Bytes x >> 4
Rot, Grün, Blau aus 0xRRGGBB (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff
und wieder zusammen (r << 16) | (g << 8) | b

Zwei Stellen, an denen die Sprachen sich unterscheiden:

  • ~ dreht in Python und Ruby unendlich viele Bits um, das Ergebnis ist negativ: ~5 ist −6. Für ein Byte schreib ~x & 0xff (250).
  • In C und JavaScript bindet & schwächer als ==: x & 1 == 0 heißt dort x & (1 == 0) und ist immer 0. In Python und Ruby ist es (x & 1) == 0. Schieben bindet überall schwächer als +: 1 << n + 1 ist 1 << (n + 1). Klammern schaden nie.

8. Zum Nachschlagen