Salta al contenuto
Note per Studenti Rappresentazione binaria dei dati

Rappresentazione binaria dei dati

In questa pagina 6

In memoria tutto è una sequenza di bit; il significato (intero, reale, carattere, istruzione) dipende da come il programma la interpreta. Questo spiega comportamenti che si vedono nei tipi di Python e C: overflow degli int in C, 0.1 + 0.2 != 0.3, dimensione delle stringhe in byte.

Basi di numerazione

Un numero in base bb con cifre cn−1…c1c0c_{n-1} \dots c_1 c_0 vale

∑i=0n−1ci bi\sum_{i=0}^{n-1} c_i \, b^i

Base Cifre Prefisso Python/C Esempio (= 45)
2 0–1 0b 0b101101
8 0–7 0o (Python), 0 (C) 0o55
10 0–9 — 45
16 0–9, A–F 0x 0x2D
  • Da base 10 a base 2: divisioni successive per 2, i resti letti dal basso verso l'alto. 45→22 r1→11 r0→5 r1→2 r1→1 r0→0 r145 \to 22\,r1 \to 11\,r0 \to 5\,r1 \to 2\,r1 \to 1\,r0 \to 0\,r1, quindi 1011012101101_2.
  • Base 2 ↔ 16: ogni cifra esadecimale corrisponde a 4 bit (0010 11012=2D160010\,1101_2 = 2D_{16}). Per questo gli indirizzi di memoria e i byte si scrivono in esadecimale.
python
bin(45), oct(45), hex(45)   # ('0b101101', '0o55', '0x2d')
int("101101", 2)            # 45
int("2d", 16)               # 45

Con nn bit si rappresentano 2n2^n configurazioni: 8 bit → 256, 16 → 65 536, 32 → circa 4,3⋅1094{,}3 \cdot 10^9.

Interi senza segno

Con nn bit: valori da 00 a 2n−12^n - 1. Somma binaria in colonna con riporto; se il risultato non sta in nn bit si ha overflow e il bit di riporto finale si perde (il risultato è preso modulo 2n2^n).

Interi con segno: complemento a 2

Con nn bit il bit più significativo ha peso negativo −2n−1-2^{n-1}:

x=−cn−12n−1+∑i=0n−2ci2ix = -c_{n-1} 2^{n-1} + \sum_{i=0}^{n-2} c_i 2^i

  • Intervallo: da −2n−1-2^{n-1} a 2n−1−12^{n-1} - 1 (8 bit: da −128-128 a 127127). Asimmetrico: c'è un negativo in più.
  • Bit più significativo = 1 ⇔ numero negativo. Lo zero ha una sola rappresentazione.
  • Opposto di un numero: si invertono tutti i bit e si somma 1. Esempio a 8 bit: 5=000001015 = 00000101, inverto → 1111101011111010, +1 → 11111011=−511111011 = -5.
  • La sommatrice è la stessa degli interi senza segno: è il motivo per cui il complemento a 2 si usa ovunque.
  • Overflow: sommando due numeri dello stesso segno si ottiene un risultato di segno opposto. A 8 bit: 127+1=10000000=−128127 + 1 = 10000000 = -128.

In Python gli int hanno precisione arbitraria: niente overflow, 2**100 è esatto. In C gli interi hanno dimensione fissa (tipicamente int a 32 bit) e l'overflow sui tipi con segno è comportamento indefinito (vedi Tipi, operatori e controllo del flusso in CTipi interi e reali del C con dimensioni e limiti, conversioni implicite e cast, divisione intera, operatori di incremento, logici e bit a bit; if, switch, while, do-while, for, break e continue.Tipi, operatori e controllo del flusso in C →).

Numeri reali: virgola mobile (IEEE 754)

Un reale è rappresentato come (−1)s⋅1.m⋅2e−bias(-1)^s \cdot 1.m \cdot 2^{e - \text{bias}}:

Formato Bit totali Segno ss Esponente ee Mantissa mm Cifre decimali circa
singola precisione (float in C) 32 1 8 (bias 127) 23 7
doppia precisione (float in Python, double in C) 64 1 11 (bias 1023) 52 15–16

Conseguenze:

  • Solo i numeri della forma k/2jk / 2^j sono esatti. 0.10.1 in binario è periodico, quindi è approssimato.
  • La precisione è relativa: la distanza tra due float consecutivi cresce con il valore.
  • Valori speciali: inf, -inf, nan (risultato di operazioni indefinite; nan != nan).
python
0.1 + 0.2 == 0.3                  # False
0.1 + 0.2                         # 0.30000000000000004
import math
math.isclose(0.1 + 0.2, 0.3)      # True: confronto con tolleranza
1e308 * 10                        # inf

Regola: mai confrontare float con ==; usare una tolleranza (math.isclose o abs(a - b) < eps).

Caratteri e testo

  • ASCII: 7 bit, 128 caratteri (lettere inglesi, cifre, punteggiatura, controllo). 'A' = 65, 'a' = 97, '0' = 48: le lettere e le cifre sono consecutive.
  • Unicode: assegna un numero (code point) a ogni carattere di ogni lingua ('è' = U+00E8 = 232, '€' = U+20AC).
  • UTF-8: codifica Unicode in 1–4 byte; i caratteri ASCII occupano 1 byte ed hanno lo stesso codice. È la codifica standard per i file di testo.
python
ord("A"), chr(97)                 # (65, 'a')
len("è"), len("è".encode("utf-8"))  # (1, 2): 1 carattere, 2 byte

In C un char è 1 byte e una stringa è un array di byte: una lettera accentata in UTF-8 occupa più char (vedi Array e stringhe in CArray di dimensione fissa in memoria contigua, inizializzazione, nessun controllo sugli indici, passaggio a funzioni con la lunghezza, matrici; stringhe come array di char terminati da '\0' e funzioni di string.h.Array e stringhe in C →).

Errori tipici

  • Dimenticare che nel complemento a 2 l'intervallo è asimmetrico (−128…127-128 \dots 127, non −127…128-127 \dots 128).
  • Usare == tra float o usare float per quantità che devono essere esatte (es. importi in centesimi: meglio interi).
  • Confondere il numero di caratteri di una stringa con il numero di byte che occupa codificata.

Teoria collegata