Διαδρομές
/
Python
Python
/
Ύλη
/
Τελεστές bitwise
Τε

Τελεστές bitwise σε Python

{one: "1 άσκηση", other: "%{count} ασκήσεις"}

Σχετικά με την έννοια Τελεστές bitwise

Στο επίπεδο του υλικού, τα τρανζίστορ μπορούν να είναι μόνο ενεργά ή ανενεργά: δύο καταστάσεις που παραδοσιακά αναπαριστούμε με 1 και 0. Αυτά είναι τα binary digits, που συντομεύονται σε bits. Η εξοικείωση με τα bits και το binary είναι ιδιαίτερα σημαντική για προγραμματιστές συστημάτων που δουλεύουν σε γλώσσες χαμηλού επιπέδου.

Ωστόσο, για το μεγαλύτερο μέρος της ιστορίας της υπολογιστικής, η προτεραιότητα στον προγραμματισμό ήταν να βρεθούν ολοένα και πιο εξελιγμένοι τρόποι για να αποκρύψουμε πίσω από αφαιρέσεις αυτή τη δυαδική πραγματικότητα. Στην Python (και σε πολλές άλλες γλώσσες προγραμματισμού υψηλού επιπέδου), δουλεύουμε με int, float, string και άλλους ορισμένους τύπους, μέχρι και με μορφές ήχου και βίντεο. Αφήνουμε τα εσωτερικά της Python να αναλάβουν (τελικά) να μεταφράσουν τα πάντα σε bits.

Παρ' όλα αυτά, η χρήση τελεστών bitwise και πράξεων bitwise μπορεί μερικές φορές να προσφέρει σημαντικά πλεονεκτήματα στην ταχύτητα και την αποδοτικότητα της μνήμης, ακόμη και σε μια γλώσσα υψηλού επιπέδου όπως η Python.

Εισαγωγή και εμφάνιση δυαδικών αριθμών

Όπως είναι φυσικό, η Python επικοινωνεί με τον χρήστη χρησιμοποιώντας δεκαδικούς αριθμούς, αλλά ο προγραμματιστής μπορεί να παρακάμψει αυτή την προεπιλογή. Στην πραγματικότητα, η Python δέχεται πρόθυμα ένα int σε binary, hexadecimal ή octal μορφή και εκτελεί άνετα μαθηματικές πράξεις μεταξύ τους. Για περισσότερες λεπτομέρειες, μπορείς να δεις την έννοια binary-octal-hexadecimal.

Οι δυαδικοί αριθμοί εισάγονται με πρόθεμα 0b, όπως ακριβώς το 0x μπορεί να χρησιμοποιηθεί για δεκαεξαδικούς (οι δεκαεξαδικοί αριθμοί είναι ένας συνοπτικός τρόπος να αναπαραστήσουμε ομάδες των 4 bits), και το oct μπορεί να χρησιμοποιηθεί για οκταδικούς αριθμούς.

Υπάρχουν πολλοί τρόποι να μετατρέψεις ακέραιους σε δυαδικές συμβολοσειρές, που διαφέρουν στο αν περιλαμβάνουν το πρόθεμα 0b και στο αν υποστηρίζουν συμπλήρωση με μηδενικά στα αριστερά:

# Binary entry.
>>> 0b10111
23

# Converting an int display to binary string, with prefix.
>>> bin(23)  
'0b10111'

>>> number = 23

# Binary without prefix, padded to 8 digits.
>>> format(number, '08b')  
'00010111'

# Same format, but using an f-string.
>>> f"{number} in decimal is {number:08b} in binary and {number:x} in hex" 
'23 in decimal is 00010111 in binary and 17 in hex'

Bitwise Logic

Στην έννοια bools συζητήσαμε τους λογικούς τελεστές and, or και not που χρησιμοποιούνται με τιμές Boolean (True και False). Οι ίδιοι κανόνες λογικής ισχύουν και όταν δουλεύεις με bits.

Ωστόσο, οι bitwise αντίστοιχοι των λογικών τελεστών & (and), | (or), ~ (not) και ^ (XOR) εφαρμόζονται σε κάθε bit μιας δυαδικής αναπαράστασης, αντιμετωπίζοντας το 1 ως True ("on") και το 0 ως False ("off"). Ένα παράδειγμα με το bitwise & μπορεί να το κάνει πιο σαφές:

>>> x = 0b01100110
>>> y = 0b00101010

>>> format(x & y, '08b')
'00100010'

Μόνο οι θέσεις που έχουν 1 σε αμφότερους τους αριθμούς εισόδου γίνονται 1 στην έξοδο.

Το bitwise & χρησιμοποιείται συχνά για την απομόνωση μεμονωμένων bits μέσα σε ένα συμπαγές σύνολο τιμών True/False, όπως οι ρυθμίσεις που μπορεί να διαμορφώσει ο χρήστης σε μια εφαρμογή. Έτσι, η τιμή κάθε επιμέρους bit μπορεί να ελέγχει τη λογική του προγράμματος:

>>> number = 0b0110
>>> number & 0b0001 > 0
False

>>> number & 0b0010 > 0
True

Για το bitwise | (or), ένα 1 τίθεται στην έξοδο αν υπάρχει 1 σε οποιαδήποτε από τις εισόδους:

>>> x = 0b01100110
>>> y = 0b00101010

>>> format(x | y, '08b')
'01101110'

Με τον τελεστή ^ για το bitwise exclusive or (xor), ένα 1 τίθεται αν εμφανίζεται σε οποιαδήποτε από τις εισόδους αλλά όχι και στις δύο. Αυτό το σύμβολο μπορεί να σου φαίνεται γνωστό από την έννοια sets, όπου χρησιμοποιείται για τη συμμετρική διαφορά των set, που είναι το ίδιο με το xor όπως εφαρμόζεται στα σύνολα. Αν το xor ^ σου φαίνεται περίεργο, να ξέρεις ότι αυτή είναι μακράν η πιο συνηθισμένη πράξη στην κρυπτογραφία.

>>> x = 0b01100110
>>> y = 0b00101010

>>> format(x ^ y, '08b')
'01001100'

Τέλος, υπάρχει ο τελεστής ~ (ο χαρακτήρας tilde), που είναι ένα bitwise not που παίρνει μία μόνο είσοδο και αντιστρέφει όλα τα bits, κάτι που μπορεί να μην είναι το αποτέλεσμα που περίμενες! Κάθε 1 στην αναπαράσταση γίνεται 0 και αντίστροφα. Δες την παρακάτω ενότητα για λεπτομέρειες.

Αρνητικοί αριθμοί και δυαδική αναπαράσταση

Στη δεκαδική αναπαράσταση, ξεχωρίζουμε τους θετικούς από τους αρνητικούς αριθμούς βάζοντας ένα πρόσημο + ή - στα αριστερά των ψηφίων. Η χρήση αυτών των συμβόλων σε δυαδικό επίπεδο αποδείχθηκε αναποτελεσματική για την ψηφιακή υπολογιστική και ανέδειξε το πρόβλημα ότι το +0 δεν είναι το ίδιο με το -0.

Αντί να χρησιμοποιούν - και +, όλοι οι σύγχρονοι υπολογιστές χρησιμοποιούν μια αναπαράσταση two's complement για τους αρνητικούς αριθμούς, μέχρι και στο επίπεδο του πυριτίου. Αυτό σημαίνει ότι όλα τα bits αντιστρέφονται και ένας αριθμός ερμηνεύεται ως αρνητικός αν το αριστερότερο bit (που ονομάζεται επίσης "πιο σημαντικό bit", ή MSB) είναι 1. Οι θετικοί αριθμοί έχουν MSB 0. Αυτή η αναπαράσταση έχει το πλεονέκτημα ότι υπάρχει μόνο μία εκδοχή του μηδενός, ώστε ο προγραμματιστής να μη χρειάζεται να διαχειρίζεται το -0 και το +0.

Αυτός ο τρόπος αναπαράστασης αρνητικών και θετικών αριθμών προσθέτει μια επιπλοκή για την Python: δεν υπάρχουν έννοιες πεπερασμένων ακεραίων όπως int32 ή int64 στο εσωτερικό της ίδιας της γλώσσας. Στη "σύγχρονη" Python, τα int έχουν απεριόριστο μέγεθος (περιορίζονται μόνο από τη χωρητικότητα του υλικού), και ένας αρνητικός ή αντιστραμμένος ως προς τα bits αριθμός έχει (θεωρητικά) άπειρο πλήθος 1 στα αριστερά, όπως ακριβώς ένας θετικός αριθμός έχει απεριόριστα 0.

Αυτό κάνει δύσκολο το να δώσουμε ένα χρήσιμο παράδειγμα του bitwise not:

>>> x = 0b01100110
>>> format(x, '08b')
'01100110'

# This is a negative binary (not twos-complement display).
>>> format(~x, '08b')
'-1100111'  

 # Decimal representation.
>>> x
102

# Using the Bitwise not, with an unintuitive result.
>>> ~x
-103

Αυτό δεν είναι το 0b10011001 που θα βλέπαμε σε γλώσσες με ακεραίους σταθερού μεγέθους.

Ο τελεστής ~ λειτουργεί όπως περιμένουμε μόνο με απρόσημους τύπους byte ή ακεραίων, ή με τύπους ακεραίων σταθερού μεγέθους. Αυτοί οι αριθμητικοί τύποι υποστηρίζονται σε πακέτα τρίτων όπως τα NumPy, pandas και sympy, αλλά όχι στον πυρήνα της Python.

Στην πράξη, οι προγραμματιστές της Python χρησιμοποιούν αρκετά συχνά τους &, |, ^ και τους τελεστές ολίσθησης που περιγράφονται παρακάτω μόνο με θετικούς αριθμούς. Οι bitwise πράξεις με αρνητικούς αριθμούς είναι πολύ πιο σπάνιες. Μια τεχνική είναι να προσθέσεις το 2**32 (or 1 << 32) σε μια αρνητική τιμή για να κάνεις ένα int απρόσημο, αλλά αυτό γίνεται δύσκολο στη διαχείριση. Μια άλλη στρατηγική είναι να δουλέψεις με τη μονάδα ctypes και να χρησιμοποιήσεις ακέραιους τύπους στυλ C, αλλά κι αυτό είναι εξίσου δυσκίνητο.

Shift operators

Ο τελεστής αριστερής ολίσθησης x << y μετακινεί όλα τα bits του x κατά y θέσεις προς τα αριστερά, γεμίζοντας τα νέα κενά με μηδενικά. Σημείωσε ότι αυτό είναι αριθμητικά ταυτόσημο με τον πολλαπλασιασμό ενός αριθμού επί (2**y).

Ο τελεστής δεξιάς ολίσθησης x >> y κάνει το αντίθετο. Αυτό είναι αριθμητικά ταυτόσημο με την ακέραια διαίρεση x // (2**y).

Να θυμάσαι την προηγούμενη ενότητα για τους αρνητικούς αριθμούς και τις παγίδες τους όταν κάνεις ολίσθηση σε αυτούς στην Python.

>>> x = 8
>>> format(x, '08b')
'00001000'

# A left bit shift. 
>>> x << 2  
32

>>> format(x << 2, '08b')
'00100000'

# A right bit shift. 
>>> format(x >> 2, '08b')
'00000010'
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα