Tracce
/
Python
Python
/
Programma
/
Operatori bit a bit
Op

Operatori bit a bit in Python

{one: "1 esercizio", many: "%{count} esercizi", other: "%{count} esercizi"}

Informazioni su Operatori bit a bit

A livello hardware, i transistor possono essere solo accesi o spenti: due stati che tradizionalmente rappresentiamo con 1 e 0. Queste sono le binary digits, abbreviate in bits. Avere familiarità con i bits e il binary è particolarmente importante per i programmatori di sistemi che lavorano con linguaggi di basso livello.

Tuttavia, per gran parte della storia dell'informatica, la priorità della programmazione è stata trovare modi sempre più sofisticati per astrarre questa realtà binaria. In Python (e in molti altri linguaggi di programmazione di alto livello), lavoriamo con int, float, string e altri tipi definiti, fino a includere formati audio e video. Lasciamo che i meccanismi interni di Python si occupino di (prima o poi) tradurre tutto in bit.

Ciononostante, usare gli operatori bit a bit e le operazioni bit a bit a volte può offrire vantaggi significativi in termini di velocità ed efficienza della memoria, anche in un linguaggio di alto livello come Python.

Inserire e visualizzare i numeri binari

Come era prevedibile, Python interagisce con l'utente usando numeri decimali, ma chi programma può sovrascrivere questa impostazione predefinita. In effetti, Python accetta senza problemi un int in formato binary, hexadecimal o octal, ed esegue volentieri operazioni matematiche tra loro. Per maggiori dettagli, puoi consultare il concetto binary-octal-hexadecimal.

I numeri binari si inseriscono con il prefisso 0b, proprio come 0x si usa per l'esadecimale (i numeri esadecimali sono un modo conciso per rappresentare gruppi di 4 bit) e oct per i numeri ottali.

Esistono diversi modi per convertire i numeri interi in stringhe binarie, che differiscono per l'inclusione del prefisso 0b e per il supporto del riempimento a sinistra con zeri:

# 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

Nel concetto bools, abbiamo parlato degli operatori logici and, or e not usati con valori booleani (True e False). Le stesse regole logiche si applicano quando si lavora con i bit.

Tuttavia, gli equivalenti bit a bit degli operatori logici & (and), | (or), ~ (not) e ^ (XOR) si applicano a ogni bit di una rappresentazione binaria, trattando 1 come True («acceso») e 0 come False («spento»). Un esempio con l'operatore & bit a bit potrebbe chiarirlo meglio:

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

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

Solo le posizioni con un 1 in entrambi i numeri di input vengono impostate a 1 nell'output.

L'operatore & bit a bit è comunemente usato per isolare singoli bit in un insieme compatto di valori True/False, come le impostazioni configurabili dall'utente in un'app. Questo permette al valore dei singoli bit di controllare la logica del programma:

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

>>> number & 0b0010 > 0
True

Per l'operatore | bit a bit (or), nell'output viene impostato un 1 se c'è un 1 in uno qualsiasi dei due input:

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

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

Con l'operatore ^ per l'or esclusivo (xor) bit a bit, viene impostato un 1 se compare in uno qualsiasi degli input ma non in entrambi. Questo simbolo potrebbe sembrarti familiare dal concetto sets, dove è usato per la differenza simmetrica dei set, che è la stessa cosa dello xor applicato agli insiemi. Se lo xor ^ ti sembra strano, sappi che è di gran lunga l'operazione più comune in crittografia.

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

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

Infine, c'è l'operatore ~ (il carattere tilde), che è un not bit a bit che prende un singolo input e inverte tutti i bit, il che potrebbe non essere il risultato che ti aspettavi! Ogni 1 nella rappresentazione diventa 0, e viceversa. Vedi la sezione qui sotto per i dettagli.

Numeri negativi e rappresentazione binaria

Nella rappresentazione decimale, distinguiamo i numeri positivi e negativi usando un segno + o - a sinistra delle cifre. Usare questi simboli a livello binario si è rivelato inefficiente per il calcolo digitale e ha sollevato il problema che +0 non è uguale a -0.

Invece di usare - e +, tutti i computer moderni usano una rappresentazione in two's complement per i numeri negativi, fino al livello del chip di silicio. Ciò significa che tutti i bit vengono invertiti e un numero viene interpretato come negativo se il bit più a sinistra (chiamato anche «bit più significativo», o MSB) è un 1. I numeri positivi hanno un MSB pari a 0. Questa rappresentazione ha il vantaggio di avere una sola versione dello zero, così chi programma non deve gestire -0 e +0.

Questo modo di rappresentare i numeri negativi e positivi aggiunge una complicazione per Python: nel linguaggio principale non esistono concetti di interi a dimensione fissa come int32 o int64. Nel Python «moderno», gli int hanno dimensione illimitata (limitata solo dalla capacità dell'hardware), e un numero negativo o invertito a livello di bit ha un numero (teoricamente) infinito di 1 a sinistra, proprio come un numero positivo ha infiniti 0.

Questo rende difficile dare un esempio utile di 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

Questo non è il 0b10011001 che vedremmo in linguaggi con interi a dimensione fissa.

L'operatore ~ funziona come previsto solo con tipi byte o interi senza segno, oppure con tipi interi a dimensione fissa. Questi tipi numerici sono supportati in pacchetti di terze parti come NumPy, pandas e sympy, ma non nel Python di base.

In pratica, i programmatori Python usano molto spesso &, |, ^ e gli operatori di shift descritti di seguito solo con numeri positivi. Le operazioni bit a bit con numeri negativi sono molto meno comuni. Una tecnica consiste nell'aggiungere 2**32 (or 1 << 32) a un valore negativo per rendere un int senza segno, ma diventa difficile da gestire. Un'altra strategia è lavorare con il modulo ctypes e usare tipi interi in stile C, ma è altrettanto scomodo.

Shift operators

L'operatore di shift a sinistra x << y sposta tutti i bit di x di y posizioni verso sinistra, riempiendo i nuovi spazi con zeri. Nota che questo è aritmeticamente identico a moltiplicare un numero per (2**y).

L'operatore di shift a destra x >> y fa l'opposto. Questo è aritmeticamente identico alla divisione intera x // (2**y).

Tieni presente la sezione precedente sui numeri negativi e le loro insidie quando li si sposta in 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'
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda