Vai al contenuto
Fondamenti di Informatica

Rappresentazione dei dati: binario, esadecimale, complemento a due e caratteri

Sistema binario ed esadecimale, complemento a due, overflow in C con esempi, tipi a larghezza fissa, virgola fissa, ASCII e UTF-8, operatori sui bit.

Dentro un computer tutto è fatto di bit: numeri, lettere, immagini, istruzioni. Lo stesso gruppo di 8 bit, 01000001, può essere il numero 65, la lettera A o un pezzo di un’istruzione, a seconda di come lo si interpreta. In questa lezione vediamo come si rappresentano i numeri interi con e senza segno, il complemento a due, la notazione esadecimale, i caratteri e i problemi di overflow che ne derivano.

Il sistema binario

In base 2 ogni cifra vale una potenza di 2: (1011)_2 = 1\cdot 8 + 0\cdot 4 + 1\cdot 2 + 1\cdot 1 = 11. Per convertire un numero decimale in binario si divide ripetutamente per 2 e si leggono i resti dal basso: 13 → 6 resto 1, 3 resto 0, 1 resto 1, 0 resto 1, quindi 13 = (1101)_2.

Con n bit si rappresentano 2^n valori: con 8 bit (un byte) da 0 a 255, con 16 bit da 0 a 65 535, con 32 bit da 0 a circa 4,3 miliardi.

L’esadecimale

I numeri binari sono lunghi e difficili da leggere. La base 16 usa le cifre 0-9 e A-F (A = 10, …, F = 15), e ogni cifra esadecimale corrisponde esattamente a 4 bit. La conversione è immediata, a gruppi di quattro:

Binario1010001111110000
EsadecimaleA3F0

Quindi (1010\,0011\,1111\,0000)_2 = \text{0xA3F0}. In C i numeri esadecimali si scrivono con il prefisso 0x. Gli indirizzi di memoria, i colori nelle pagine web (#FF8000) e i valori dei registri nei datasheet si scrivono in esadecimale.

Numeri con segno: il complemento a due

Il modo naturale di rappresentare i negativi, un bit di segno e il modulo, ha due difetti: due zeri (+0 e −0) e circuiti di somma diversi per i positivi e i negativi. Tutti i processori usano invece il complemento a due: con n bit, il bit più significativo pesa -2^{n-1} invece di +2^{n-1}.

Ruota del complemento a due su 4 bit con i codici binari e i valori con segno
Con 4 bit i 16 codici rappresentano i numeri da −8 a +7. Andando avanti di uno sulla ruota si somma 1; da 0111 (+7) si passa a 1000 (−8): è l’overflow.

Con 4 bit: (1011)_2 = -8 + 2 + 1 = -5. Gli intervalli sono da -2^{n-1} a 2^{n-1} - 1: con 8 bit da −128 a 127, con 32 bit da circa −2,1 a +2,1 miliardi.

Come si cambia segno. Si invertono tutti i bit e si somma 1. Da +5 = 0101: inverto, 1010; sommo 1, 1011 = −5. Verifica: 0101 + 1011 = 1\,0000, e il riporto oltre il quarto bit si scarta: 0.

Il vantaggio. La somma funziona allo stesso modo per positivi e negativi: lo stesso circuito sommatore fa 7 + (-3) sommando 0111 + 1101 = 1\,0100, cioè 4 scartando il riporto. La sottrazione diventa la somma dell’opposto.

L’overflow

Se il risultato non sta nei bit disponibili, “fa il giro” della ruota. Con 8 bit con segno, 127 + 1 = -128. In C:

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    uint8_t u = 250;
    u = u + 10;               /* senza segno: 260 - 256 = 4 */
    printf("%u\n", u);        /* stampa 4 */

    int16_t t = 30000;
    t = t + 10000;            /* con segno: il risultato non sta in 16 bit */
    printf("%d\n", t);        /* su quasi tutti i sistemi stampa -25536 */
    return 0;
}

Per i tipi senza segno il C garantisce l’aritmetica modulo 2^n. Per quelli con segno l’overflow è un comportamento non definito: il compilatore può assumere che non avvenga, e i risultati possono essere sorprendenti. È una delle sorgenti di bug più comuni nel firmware: un contatore di millisecondi a 32 bit senza segno torna a zero dopo circa 49,7 giorni, e il codice che confronta i tempi deve tenerne conto.

I tipi a larghezza fissa di <stdint.h> (uint8_t, int16_t, uint32_t, …) sono da preferire nei programmi per microcontrollori, perché la dimensione di int cambia da un processore all’altro: 16 bit su un AVR, 32 su un ARM.

I numeri reali

I numeri con la virgola si rappresentano in virgola mobile secondo lo standard IEEE 754 (tipi float e double in C), descritto nella lezione di Calcolo numerico sugli errori. Nei microcontrollori senza unità di calcolo in virgola mobile si usa spesso la virgola fissa: si lavora con interi che rappresentano, per esempio, millesimi (una tensione di 3,301 V diventa 3301 mV).

I caratteri

Un carattere è un numero secondo una tabella di codifica. Il codice ASCII (1963) usa 7 bit per 128 caratteri: ‘A’ = 65, ‘a’ = 97, ‘0’ = 48, lo spazio = 32, il ritorno a capo = 10. In C un char è un piccolo intero, e si può fare aritmetica sui caratteri: '7' - '0' vale 7, ed è il modo standard di convertire una cifra in numero.

Per tutti gli alfabeti del mondo c’è Unicode, con oltre 150 000 caratteri, codificato quasi sempre in UTF-8: i caratteri ASCII occupano un byte, le lettere accentate due (è = 0xC3 0xA8), gli ideogrammi tre, le emoji quattro. È per questo che una stringa C con “perché” occupa 7 byte e non 6.

Gli operatori sui bit

Il C permette di lavorare sui singoli bit con gli operatori & (AND), | (OR), ^ (XOR), ~ (NOT), << e >> (scorrimento). Uno scorrimento a sinistra di k posizioni moltiplica per 2^k: x << 3 vale x * 8. Sono fondamentali per i protocolli di comunicazione e per i registri delle periferiche, come vedremo nella lezione sul C per i sistemi embedded.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x
()
x