Successivo: , Precedente: , Su: Funzioni predefinite   [Contenuti][Indice]


9.1.6 Funzioni per operazioni di manipolazione bit

Io posso spiegarlo per te, ma non posso capirlo per te.

Anonimo

Molti linguaggi consentono di eseguire operazioni bit a bit su due numeri interi. In altre parole, l’operazione è eseguita su ogni successiva coppia di bit presi da ognuno dei due operandi. Tre operazioni comuni sono AND, OR e XOR bit a bit. Queste operazioni sono descritte nella Tabella 9.6.

             Operatore booleano
          |  AND  |   OR  |  XOR
          |---+---+---+---+---+---
Operandi  | 0 | 1 | 0 | 1 | 0 | 1
----------+---+---+---+---+---+---
    0     | 0   0 | 0   1 | 0   1
    1     | 0   1 | 1   1 | 1   0

Tabella 9.6: Operazioni a livello di bit

Come si vede, il risultato di un’operazione di AND è 1 solo quando entrambi i bit sono 1. Il risultato di un’operazione di OR è 1 se almeno un bit è 1. Il risultato di un’operazione di XOR è 1 se l’uno o l’altro bit è 1, ma non tutti e due. La successiva operazione è il complemento; il complemento di 1 è 0 e il complemento di 0 è 1. Quindi, quest’operazione “inverte” tutti i bit di un dato valore.

Infine, due altre operazioni comuni consistono nello spostare i bit a sinistra o a destra. Per esempio, se si ha una stringa di bit ‘10111001’ e la si sposta a destra di tre bit, si ottiene ‘00010111’.60 Partendo nuovamente da ‘10111001’ e spostandolo a sinistra di tre bit, si ottiene ‘11001000’. La lista seguente descrive le funzioni predefinite di gawk che rendono disponibili le operazioni a livello di bit. I parametri opzionali sono racchiusi tra parentesi quadre ([ ]):

and(v1, v2 [, …])

Restituisce l’AND bit a bit degli argomenti. Gli argomenti devono essere almeno due.

compl(val)

Restituisce il complemento bit a bit di val.

lshift(val, contatore)

Restituisce il valore di val, spostato a sinistra di contatore bit.

or(v1, v2 [, …])

Restituisce l’OR bit a bit degli argomenti. Gli argomenti devono essere almeno due.

rshift(val, contatore)

Restituisce il valore di val, spostato a destra di contatore bit.

xor(v1, v2 [, …])

Restituisce il XOR bit a bit degli argomenti. Gli argomenti devono essere almeno due.

ATTENZIONE: A partire dalla versione di gawk versione 4.2, gli operandi negativi non sono consentiti per nessuna di queste funzioni. Un operando negativo produce un errore fatale. Si veda la nota a lato “Attenzione. Non è tutto oro quel che luccica!” per maggiori informazioni sul perché.

Ecco una funzione definita dall’utente (vedi la sezione Funzioni definite dall’utente) che illustra l’uso di queste funzioni:

# bits2str --- decodifica un numero intero in una serie di 0/1 leggibili

function bits2str(byte,        dati, maschera)
{
    if (byte == 0)
        return "0"

    maschera = 1
    for (; byte != 0; stringa = rshift(stringa, 1))
        dati = (and(byte, maschera) ? "1" : "0") dati

    while ((length(dati) % 8) != 0)
        dati = "0" dati

    return dati
}

BEGIN {
    printf "123 = %s\n", bits2str(123)
    printf "0123 = %s\n", bits2str(0123)
    printf "0x99 = %s\n", bits2str(0x99)
    comp = compl(0x99)
    printf "compl(0x99) = %#x = %s\n", comp, bits2str(comp)
    shift = lshift(0x99, 2)
    printf "lshift(0x99, 2) = %#x = %s\n", shift, bits2str(shift)
    shift = rshift(0x99, 2)
    printf "rshift(0x99, 2) = %#x = %s\n", shift, bits2str(shift)
}

Questo programma produce il seguente output quando viene eseguito:

$ gawk -f testbits.awk
-| 123 = 01111011
-| 0123 = 01010011
-| 0x99 = 10011001
-| compl(0x99) = 0x3fffffffffff66 =
-| 00111111111111111111111111111111111111111111111101100110
-| lshift(0x99, 2) = 0x264 = 0000001001100100
-| rshift(0x99, 2) = 0x26 = 00100110

La funzione bits2str() trasforma un numero binario in una stringa. Inizializzando maschera a uno otteniamo un valore binario in cui il bit più a destra è impostato a uno. Usando questa maschera, la funzione continua a controllare il bit più a destra. l’operazione di AND tra la maschera e il valore indica se il bit più a destra è uno oppure no. Se questo è il caso, un "1" è concatenato all’inizio della stringa. Altrimenti, è concatenato uno "0". Il valore è quindi spostato a destra di un bit e il ciclo continua finché non ci sono più bit.

Se il valore iniziale è zero, viene restituito semplicemente uno "0". Altrimenti, alla fine, al valore ottenuto vengono aggiunti degli zeri a sinistra, per arrivare a stringhe di lunghezza multipla di 8, ossia contenenti un numero intero di byte. Questo è tipico dei computer moderni.

Il codice principale nella regola BEGIN mostra la differenza tra i valori decimale e ottale dello stesso numero. (vedi la sezione Numeri ottali ed esadecimali), e poi mostra i risultati delle funzioni compl(), lshift() e rshift().

Attenzione. Non è tutto oro quel che luccica!

In altri linguaggi, le operazioni "bit a bit" sono eseguite su valori interi, non su valori in virgola mobile. Come regola generale, tali operazioni funzionano meglio se eseguite su interi senza segno.

gawk tenta di trattare gli argomenti delle funzioni "bit a bit" come interi senza segno. Per questo motivo, gli argomenti negativi provocano un errore fatale.

In una normale operazione, per tutte queste funzioni, prima il valore in virgola mobile a doppia precisione viene convertito nel tipo intero senza segno di C più ampio, poi viene eseguita l’operazione "bit a bit". Se il risultato non può essere rappresentato esattamente come un tipo double di C, vengono rimossi i bit iniziali diversi da zero uno alla volta finché non sono rappresentati esattamente. Il risultato è poi nuovamente convertito in un tipo double di C.61

Comunque, quando si usa il calcolo con precisione arbitraria con l’opzione -M (vedi la sezione Calcolo con precisione arbitraria con gawk), il risultato può essere diverso. Questo è particolarmente evidente con la funzione compl():

$ gawk 'BEGIN { print compl(42) }'
-| 9007199254740949
$ gawk -M 'BEGIN { print compl(42) }'
-| -43

Quel che avviene diventa chiaro quando si stampano i risultati in notazione esadecimale:

$ gawk 'BEGIN { printf "%#x\n", compl(42) }'
-| 0x1fffffffffffd5
$ gawk -M 'BEGIN { printf "%#x\n", compl(42) }'
-| 0xffffffffffffffd5

Quando si usa l’opzione -M, nel dettaglio, gawk usa gli interi a precisione arbitraria di GNU MP che hanno almeno 64 bit di precisione. Quando non si usa l’opzione -M, gawk memorizza i valori interi come regolari valori in virgola mobile con doppia precisione, che mantengono solo 53 bit di precisione. Inoltre, la libreria GNU MP tratta (o almeno sembra che tratti) il bit iniziale come un bit con segno; così il risultato con -M in questo caso è un numero negativo.

In breve, usare gawk per qualsiasi tipo di operazione "bit a bit", tranne le più semplici, probabilmente è una cattiva idea; caveat emptor!


Note a piè di pagina

(60)

Questo esempio presuppone che degli zeri riempiano le posizioni a sinistra. Per gawk, è sempre così, ma in alcuni linguaggi è possibile che le posizioni a sinistra siano riempite con degli uno.

(61)

Per essere più chiari, la conseguenza è che gawk può memorizzare solo un determinato intervallo di valori interi; i numeri al di fuori di questo intervallo vengono ridotti per rientrare all’interno dell’intervallo.


Successivo: , Precedente: , Su: Funzioni predefinite   [Contenuti][Indice]