Bei Python's `int` kann man sich bei positiven Zahlen in der Binärdarstellung immer unendlich viele führende 0en vorstellen, und bei negativen Zahlen unendlich viele führende 1en.
Code: Alles auswählen
In [551]: -1 & (1 << 1000) != 0
Out[551]: True
In [552]: 1 & (1 << 1000) != 0
Out[552]: False
In [553]: 1 & (1 << 10000) != 0
Out[553]: False
In [554]: 1 & (1 << 1000000) != 0
Out[554]: False
In [555]: -1 & (1 << 1000) != 0
Out[555]: True
In [556]: -1 & (1 << 10000) != 0
Out[556]: True
In [557]: -1 & (1 << 100000) != 0
Out[557]: True
Es ging mir bei der Beschreibung der Varianten nicht nur das Bit an dem man eine negative Zahl erkennen kann, sondern insgesamt um die Kodierung von negativen Zahlen. Das sind so drei mehr oder weniger naheliegende Möglichkeiten negative Zahlen mit Bits zu kodieren.
Mal als Übersicht wie das mit 4 Bits aussieht. Erste Spalte die „sign extension“ die man machen muss, wenn man mehr als 4 Bit verwendet. Dann die Bits selbst. Der Dezimalwert der vier Bits ohne Vorzeichen, und dann der Wert, der das Bitmuster bei den drei Varianten hätte. Das Programm:
Code: Alles auswählen
#!/usr/bin/env python3
def is_negative(value):
return bool(value & 0b1000)
def negative_bit(value):
return (value & 0b0111) * (-1 if is_negative(value) else 1)
def ones_complement(value):
return -(value ^ 0b1111) if is_negative(value) else value
def twos_complement(value):
return -((value ^ 0b1111) + 1) if is_negative(value) else value
def main():
print("sgn.ext bits u -bit 1's 2's")
print("===============================")
for i in range(16):
print(
f" ...{'111' if is_negative(i) else '000'}" # sign extend
f" {i:04b}" # bit pattern
f" {i:2}" # unsigned decimal value
f" {negative_bit(i):3}"
f" {ones_complement(i):3}"
f" {twos_complement(i):3}"
)
if __name__ == "__main__":
main()
Die ausgegebene Tabelle:
Code: Alles auswählen
sgn.ext bits u -bit 1's 2's
===============================
...000 0000 0 0 0 0
...000 0001 1 1 1 1
...000 0010 2 2 2 2
...000 0011 3 3 3 3
...000 0100 4 4 4 4
...000 0101 5 5 5 5
...000 0110 6 6 6 6
...000 0111 7 7 7 7
...111 1000 8 0 -7 -8
...111 1001 9 -1 -6 -7
...111 1010 10 -2 -5 -6
...111 1011 11 -3 -4 -5
...111 1100 12 -4 -3 -4
...111 1101 13 -5 -2 -3
...111 1110 14 -6 -1 -2
...111 1111 15 -7 0 -1
Der Grund warum negative ganze Zahlen in Digitalrechnern in aller Regel als Zweierkomplement kodiert sind, und nicht beispielsweise als Einerkomplement oder als positive Zahlen mit einem Vorzeichenbit, ist das die simpelste Addiererschaltung sowohl für Zahlen ohne Vorzeichen als auch für welche mit Vorzeichen funktioniert. Für die anderen Darstellungen müsste man sonst entweder in Hard- oder Software zusätzliche Schritte machen und zwischen „signed“ und „unsigned“ Additionen unterscheiden.
Beispiel von einem ARM 16-Bit Assembler-Programm das Bytewerte addiert und die Rechnung in Binärdarstellung ausgibt:
Code: Alles auswählen
.syntax unified
.global _start
@
@ Add bytes and print the calculation as binary numbers.
@
@
@ r0-r2 and r7 stay the same throughout the program up until the exit
@ instruction sequence. r3 is a pointer to the current number pair.
@
.thumb_func
_start:
movs r7, #4 @ Only system call is write.
movs r0, #1 @ Write to stdout.
ldr r1, =character @ Address of the character to write.
movs r2, #1 @ Write one character.
adr r3, numbers @ Set pointer to first number pair.
loop:
movs r4, #' ' @ Print space followed by first number.
strb r4, [r1]
svc 0
ldrb r6, [r3, #0]
bl print_binary
movs r4, #'+' @ Print '+' followed by second number.
strb r4, [r1]
svc 0
ldrb r6, [r3, #1]
bl print_binary
movs r4, #'=' @ Print '=', add numbers,
strb r4, [r1] @ and print result.
svc 0
ldrb r4, [r3, #0]
adds r6, r4
bl print_binary
movs r4, #10 @ Print line feed.
strb r4, [r1]
svc 0
adds r3, #2 @ Advance number pair pointer and check
adr r6, numbers_end @ if loop is done.
cmp r6, r3
bne loop
movs r7, #1 @ System call to exit program
movs r0, #0 @ with exit code 0.
svc 0
@
@ Print number in r6 as binary number.
@
print_binary:
movs r5, #128 @ Init bit mask for current bit = MSB.
1: tst r6, r5 @ Test bit and print '0' or '1'.
ite eq
moveq r4, #'0'
movne r4, #'1'
strb r4, [r1]
svc 0
lsrs r5, #1 @ Shift bit mask to the next bit.
bne 1b
movs r4, #10 @ Print line feed.
strb r4, [r1]
svc 0
blx lr
@ Number pairs to be added.
numbers:
.byte 3, 5
.byte -2, 7
.byte 12, -1
.byte 5, -10
numbers_end:
.bss
character:
.space 1 @ Character to be printed.
Ausgabe:
Code: Alles auswählen
00000011
+00000101
=00001000
11111110
+00000111
=00000101
00001100
+11111111
=00001011
00000101
+11110110
=11111011
Bei den Ergebnissen ist es egal ob man die jetzt als vorzeichenlos oder mit Vorzeichen in Zweierkomplmentdarstellung betrachtet.