Materiale Ingegneria Informatica
Rappresentazione e Algoritmi
Il Concetto di Algoritmo
1. Definizione
Un Algoritmo è una sequenza precisa (non ambigua) e finita di operazioni che porta alla realizzazione di un compito o alla risoluzione di un problema.
L'esecuzione delle azioni nell'ordine specificato consente di trasformare i dati di partenza (input) nei risultati desiderati (output).
2. Proprietà Fondamentali
Affinché una procedura possa essere considerata un algoritmo, deve soddisfare i seguenti requisiti:
- Atomicità: Ogni istruzione deve essere elementare, ovvero non ulteriormente scomponibile.
- Non ambiguità (Univocità): I passi devono essere interpretabili in un unico modo, senza incertezze per l'esecutore.
- Finitezza: L'algoritmo deve essere composto da un numero discreto di passi e deve terminare dopo un tempo finito.
- Determinismo: Partendo dagli stessi dati di input, l'algoritmo deve produrre ogni volta i medesimi risultati.
- Generalità: L'algoritmo deve risolvere una classe di problemi e non un singolo caso specifico (es: deve saper sommare due numeri qualsiasi, non solo 5 e 3).
Le 3 Categorie di Operazioni
Le operazioni elementari utilizzate negli algoritmi appartengono a una di queste categorie:
- 1. Operazioni Sequenziali:
Realizzano una singola azione. Quando l'azione è terminata, si passa automaticamente all'istruzione successiva nella lista. - 2. Operazioni Condizionali:
Controllano una condizione (Vero/Falso). In base al valore della condizione, selezionano quale operazione eseguire successivamente (bivi logici). - 3. Operazioni Iterative (Cicli):
Ripetono l'esecuzione di un blocco di operazioni finché non viene verificata una determinata condizione.
Flusso di Risoluzione del Problema
Risolvere un problema significa produrre risultati a partire da dati in ingresso. L'algoritmo è lo strumento che guida l'esecutore in questo processo.
(Ingresso)
(Applica l'Algoritmo)
(Uscita)
Generalità dell'Algoritmo:
L'algoritmo deve essere applicabile a un qualsiasi insieme di dati appartenenti al suo dominio di definizione.
Esempio: Se un algoritmo somma numeri interi, deve funzionare correttamente sia per interi positivi che per interi negativi, non solo per un caso specifico.
Programmazione e Linguaggi
Il Programma
La formulazione testuale di un algoritmo in un linguaggio comprensibile ad un calcolatore è detta PROGRAMMA.
Fasi di Risoluzione:
- Individuazione di un procedimento risolutivo.
- Scomposizione del procedimento in un insieme ordinato di azioni → ALGORITMO.
- Rappresentazione dei dati e dell’algoritmo attraverso un formalismo comprensibile al calcolatore → LINGUAGGIO DI PROGRAMMAZIONE.
Linguaggi di Programmazione
Un Linguaggio di Programmazione è una notazione formale usata per descrivere algoritmi. Definisce quali sono gli elementi linguistici primitivi, le frasi lecite e se una frase appartiene al linguaggio.
Perché non usare il Linguaggio Naturale?
- Non evita le ambiguità (una frase può avere più significati).
- Non si presta a descrivere processi computazionali automatizzabili.
Sintassi e Semantica
Ogni linguaggio è caratterizzato da:
1. SINTASSI (La Forma)
Insieme di regole formali per la scrittura di programmi. Fissano le modalità per costruire frasi corrette nel linguaggio.
2. SEMANTICA (Il Significato)
Insieme dei significati da attribuire alle frasi (sintatticamente corrette) costruite nel linguaggio.
Può essere descritta in vari modi:
- A parole: Spesso poco precisa e ambigua.
- Semantica Operazionale: Mediante le azioni eseguite.
- Semantica Denotazionale: Mediante funzioni matematiche.
- Semantica Assiomatica: Mediante formule logiche.
Grammatiche
Una Grammatica è definita dalla quadrupla:
G = <V, VN, P, S>
- V: insieme finito di simboli terminali (entità atomiche e predefinite).
- VN: insieme finito di simboli non terminali (detti anche "categorie sintattiche").
- P: insieme finito di regole di produzione.
- S: simbolo non terminale detto simbolo iniziale.
Data una grammatica G, si dice Linguaggio LG generato da G l’insieme delle frasi di V:
- Derivabili dal simbolo iniziale S.
- Applicando le regole di produzione P.
Le frasi di un linguaggio di programmazione vengono dette programmi.
Grammatica BNF (Backus-Naur Form)
È una grammatica le cui regole di produzione sono della forma:
Dove X è un simbolo non terminale ed A1...An è una sequenza di simboli (terminali o non terminali).
Una grammatica BNF definisce un linguaggio sull’alfabeto terminale V mediante un meccanismo di derivazione (riscrittura).
Si dice che A deriva da X se esiste una sequenza di derivazioni da X ad A.
Costrutto "one of":
Indica che X può assumere una unica alternativa tra A1, ..., An.
Esempio di Grammatica BNF
Definizione G = <V, VN, P, S>:
- V = {lupo, canarino, bosco, cielo, mangia, vola, canta, ., il, lo} (Simboli terminali)
- VN = {frase, soggetto, verbo, articolo, nome} (Categorie sintattiche)
- S = frase
Regole di Produzione P:
frase → soggetto verbo .
soggetto → articolo nome
articolo → one of { il, lo }
nome → one of { lupo, canarino, bosco, cielo }
verbo → one of { mangia, vola, canta }
Esempio di Derivazione della frase "il lupo mangia .":
frase → soggetto verbo .
→ articolo nome verbo .
→ il nome verbo .
→ il lupo verbo .
→ il lupo mangia .
Altri Costrutti del Metalinguaggio
Oltre alle regole di base, il metalinguaggio BNF offre costrutti comodi per semplificare la scrittura delle grammatiche e renderle più compatte.
1. Presenza Opzionale (opt)
Indica che un simbolo (terminale o non terminale) è opzionale: può apparire nella frase oppure essere omesso.
Regola: X → A1opt A2
Indica che X può essere riscritto in due modi:
A1 A2(se A1 è presente)A2(se A1 è assente)
Analogamente, la regola X → A1 A2opt genera le frasi A1 A2 oppure A1.
2. Sequenza (seq)
Permette di definire una sequenza di simboli identici (ripetizione) senza dover scrivere regole ricorsive esplicite.
Sia A un simbolo. Indicheremo con A-seq una sequenza di tali simboli di lunghezza maggiore o uguale a uno.
A-seq corrisponde a:
A(lunghezza 1)A A(lunghezza 2)A A ... A(lunghezza n)
Esempio:
Se la categoria nome include simboli come {lupo, canarino, bosco}, allora la regola nome-seq può generare frasi composte da una sequenza arbitraria di nomi:
lupo canarino lupo lupo
Questa frase è coerente con la regola di produzione nome-seq.
Nota tecnica: Senza il costrutto -seq, per definire una sequenza dovremmo usare una definizione ricorsiva del tipo:
Sequenza → Elemento | Elemento Sequenza
Approccio Compilato
Nello sviluppo di software, l'approccio compilato prevede la traduzione completa del codice sorgente in linguaggio macchina prima dell'esecuzione.
Fasi dello Sviluppo
- Editing: Scrittura del testo del programma (codice sorgente) tramite un editor e memorizzazione su supporti permanenti (es. Hard Disk).
- Compilazione e Linking:
- Il Compilatore traduce il codice sorgente in codice oggetto (non ancora eseguibile).
- Il Linker unisce il codice oggetto con le librerie necessarie per creare il programma eseguibile finale.
- Esecuzione: Il sistema operativo carica l'eseguibile in memoria centrale (RAM) e la CPU lo esegue.
Il Compilatore
È il software responsabile della traduzione. Il suo lavoro si divide in due macro-fasi:
Sorgente → [ COMPILATORE ] → Codice Oggetto
(Controllo correttezza)
• Analisi Lessicale
• Analisi Sintattica
(Creazione output)
• Generazione del codice
• Ottimizzazione
Esempi di linguaggi compilati: C, C++, Fortran, Rust, Go.
L'Approccio Compilato in C++
Il C++ utilizza un modello di compilazione "ahead-of-time" (AOT). A differenza di altri linguaggi, il processo trasforma il testo in un binario specifico per l'architettura hardware di destinazione.
Dettaglio delle Fasi di Build
-
Pre-elaborazione (Preprocessing):
Prima della compilazione vera e propria, il Preprocessore analizza le direttive che iniziano con
#.- Risolve le inclusioni (
#include) copiando il contenuto degli header nel file. - Sostituisce le macro (
#define). - Gestisce la compilazione condizionale (
#ifdef).
- Risolve le inclusioni (
-
Compilazione (Analisi e Traduzione):
Il compilatore trasforma il codice espanso in linguaggio assembly e poi in codice oggetto (file
.objo.o).- Analisi Semantica: Il C++ è un linguaggio a tipizzazione forte; qui il compilatore verifica che le operazioni siano coerenti con i tipi di dati (es. non si può sommare una stringa a un intero).
- Ottimizzazione: Il compilatore riorganizza le istruzioni per massimizzare la velocità o ridurre l'occupazione di memoria.
-
Linking (Collegamento):
Il Linker è l'architetto finale. Spesso un progetto C++ è diviso in molti file;
il linker:- Risolve i riferimenti esterni (se il file A usa una funzione definita nel file B).
- Collega le Librerie Standard (es.
iostream). - Genera il file Eseguibile (
.exesu Windows,outsu Unix).
Dove avviene l'errore?
Capire in quale fase si interrompe il processo è fondamentale per il debugging:
| Tipo di Errore | Fase | Causa tipica |
|---|---|---|
| Sintassi | Compilazione | Errori di grammatica del codice (es. manca un ; o una }). |
| Linker (LNK) | Linking | Funzioni dichiarate ma non trovate (es. librerie mancanti). |
| Runtime | Esecuzione | Operazioni illegali durante l'uso (es. divisione per zero). |
| Logico | Esecuzione | Il programma gira ma produce risultati sbagliati (errore dell'algoritmo). |
Approccio Interpretato
A differenza dell'approccio compilato, l'approccio interpretato non produce un file eseguibile separato prima dell'uso. Il codice sorgente viene analizzato ed eseguito direttamente, istruzione per istruzione, da un programma chiamato Interprete.
Fasi dello Sviluppo
- Editing: Scrittura del testo del programma e memorizzazione su supporti permanenti.
- Interpretazione: L'interprete legge il sorgente e lo esegue "al volo".
L'Interprete
L'interprete cicla continuamente attraverso queste due fasi per ogni porzione di codice:
Sorgente → [ INTERPRETE ] → Risultato
(Del codice sorgente)
• Analisi Lessicale
• Analisi Sintattica
(Immediata)
L'istruzione viene eseguita dalla CPU.
Esempi di linguaggi interpretati: Python, Matlab, Javascript.
Nota sul Corso: In questo corso prenderemo in considerazione solo il linguaggio C++, e dunque, studieremo solo l'approccio compilato.
Rappresentazione dell'Informazione
L’informazione è qualcosa di astratto. Per poterla manipolare è necessario rappresentarla.
All'interno di un calcolatore, i vari tipi di informazione (testi, figure, numeri, musica, ecc.) vengono rappresentati per mezzo di sequenze di bit.
BIT (Binary DigIT)
- È l'unità di misura elementare dell'informazione.
- È la base del sistema numerico binario.
- Può assumere solo due valori: 0 oppure 1.
BYTE
È l'unità di misura dell'informazione che corrisponde a 8 bit.
[0][1][0][0][1][1][0][1] = 1 Byte
Rappresentazione dei Caratteri
Codifica ASCII
La codifica standard è l'ASCII (American Standard Code for Information Interchange). Utilizza 7 bit per rappresentare 128 caratteri (lettere inglesi, numeri, simboli e caratteri di controllo).
ASCII Esteso:
- Contiene ulteriori 128 caratteri (da 128 a 255), per un totale di 256 caratteri.
- Richiede 8 bit (ossia 1 Byte).
- Per i caratteri standard (0-127), il bit più significativo (MSB) vale 0.
NB: Una codifica alternativa moderna è UTF-8 (standard UNICODE), in cui i caratteri hanno un'occupazione variabile di 1, 2, 3 o 4 byte per rappresentare tutti i simboli del mondo.
Il linguaggio C++ classico (con il tipo char) non supporta nativamente UTF-8 multibyte in modo semplice, ma si basa sulla codifica a 1 byte (ASCII/ASCII Esteso).
Tabella ASCII Standard (Completa 0-127)
| Dec | Hex | Char | Descrizione |
|---|---|---|---|
| 0 | 00 | NUL | Null char |
| 1 | 01 | SOH | Start of Heading |
| 2 | 02 | STX | Start of Text |
| 3 | 03 | ETX | End of Text |
| 4 | 04 | EOT | End of Transmission |
| 5 | 05 | ENQ | Enquiry |
| 6 | 06 | ACK | Acknowledge |
| 7 | 07 | BEL | Bell |
| 8 | 08 | BS | Backspace |
| 9 | 09 | HT | Horizontal Tab |
| 10 | 0A | LF | Line Feed (New Line) |
| 11 | 0B | VT | Vertical Tab |
| 12 | 0C | FF | Form Feed |
| 13 | 0D | CR | Carriage Return |
| 14 | 0E | SO | Shift Out |
| 15 | 0F | SI | Shift In |
| 16 | 10 | DLE | Data Link Escape |
| 17 | 11 | DC1 | Device Control 1 |
| 18 | 12 | DC2 | Device Control 2 |
| 19 | 13 | DC3 | Device Control 3 |
| 20 | 14 | DC4 | Device Control 4 |
| 21 | 15 | NAK | Negative Acknowledge |
| 22 | 16 | SYN | Synchronous Idle |
| 23 | 17 | ETB | End of Trans. Block |
| 24 | 18 | CAN | Cancel |
| 25 | 19 | EM | End of Medium |
| 26 | 1A | SUB | Substitute |
| 27 | 1B | ESC | Escape |
| 28 | 1C | FS | File Separator |
| 29 | 1D | GS | Group Separator |
| 30 | 1E | RS | Record Separator |
| 31 | 1F | US | Unit Separator |
| 32 | 20 | [sp] | Spazio |
| 33 | 21 | ! | Punto esclamativo |
| 34 | 22 | " | Doppie virgolette |
| 35 | 23 | # | Cancelletto |
| 36 | 24 | $ | Dollaro |
| 37 | 25 | % | Percentuale |
| 38 | 26 | & | Ampersand |
| 39 | 27 | ' | Apice singolo |
| 40 | 28 | ( | Parentesi aperta |
| 41 | 29 | ) | Parentesi chiusa |
| 42 | 2A | * | Asterisco |
| 43 | 2B | + | Più |
| 44 | 2C | , | Virgola |
| 45 | 2D | - | Meno |
| 46 | 2E | . | Punto |
| 47 | 2F | / | Slash |
| 48 | 30 | 0 | Cifra 0 |
| 49 | 31 | 1 | Cifra 1 |
| 50 | 32 | 2 | Cifra 2 |
| 51 | 33 | 3 | Cifra 3 |
| 52 | 34 | 4 | Cifra 4 |
| 53 | 35 | 5 | Cifra 5 |
| 54 | 36 | 6 | Cifra 6 |
| 55 | 37 | 7 | Cifra 7 |
| 56 | 38 | 8 | Cifra 8 |
| 57 | 39 | 9 | Cifra 9 |
| 58 | 3A | : | Due punti |
| 59 | 3B | ; | Punto e virgola |
| 60 | 3C | < | Minore |
| 61 | 3D | = | Uguale |
| 62 | 3E | > | Maggiore |
| 63 | 3F | ? | Punto interrogativo |
| 64 | 40 | @ | Chiocciola |
| 65 | 41 | A | A Maiuscola |
| 66 | 42 | B | B Maiuscola |
| 67 | 43 | C | C Maiuscola |
| 68 | 44 | D | D Maiuscola |
| 69 | 45 | E | E Maiuscola |
| 70 | 46 | F | F Maiuscola |
| 71 | 47 | G | G Maiuscola |
| 72 | 48 | H | H Maiuscola |
| 73 | 49 | I | I Maiuscola |
| 74 | 4A | J | J Maiuscola |
| 75 | 4B | K | K Maiuscola |
| 76 | 4C | L | L Maiuscola |
| 77 | 4D | M | M Maiuscola |
| 78 | 4E | N | N Maiuscola |
| 79 | 4F | O | O Maiuscola |
| 80 | 50 | P | P Maiuscola |
| 81 | 51 | Q | Q Maiuscola |
| 82 | 52 | R | R Maiuscola |
| 83 | 53 | S | S Maiuscola |
| 84 | 54 | T | T Maiuscola |
| 85 | 55 | U | U Maiuscola |
| 86 | 56 | V | V Maiuscola |
| 87 | 57 | W | W Maiuscola |
| 88 | 58 | X | X Maiuscola |
| 89 | 59 | Y | Y Maiuscola |
| 90 | 5A | Z | Z Maiuscola |
| 91 | 5B | [ | Parentesi Quadra Aperta |
| 92 | 5C | \ | Backslash |
| 93 | 5D | ] | Parentesi Quadra Chiusa |
| 94 | 5E | ^ | Caretto (Esponente) |
| 95 | 5F | _ | Underscore |
| 96 | 60 | ` | Accento grave |
| 97 | 61 | a | a minuscola |
| 98 | 62 | b | b minuscola |
| 99 | 63 | c | c minuscola |
| 100 | 64 | d | d minuscola |
| 101 | 65 | e | e minuscola |
| 102 | 66 | f | f minuscola |
| 103 | 67 | g | g minuscola |
| 104 | 68 | h | h minuscola |
| 105 | 69 | i | i minuscola |
| 106 | 6A | j | j minuscola |
| 107 | 6B | k | k minuscola |
| 108 | 6C | l | l minuscola |
| 109 | 6D | m | m minuscola |
| 110 | 6E | n | n minuscola |
| 111 | 6F | o | o minuscola |
| 112 | 70 | p | p minuscola |
| 113 | 71 | q | q minuscola |
| 114 | 72 | r | r minuscola |
| 115 | 73 | s | s minuscola |
| 116 | 74 | t | t minuscola |
| 117 | 75 | u | u minuscola |
| 118 | 76 | v | v minuscola |
| 119 | 77 | w | w minuscola |
| 120 | 78 | x | x minuscola |
| 121 | 79 | y | y minuscola |
| 122 | 7A | z | z minuscola |
| 123 | 7B | { | Graffa Aperta |
| 124 | 7C | | | Barra verticale (Pipe) |
| 125 | 7D | } | Graffa Chiusa |
| 126 | 7E | ~ | Tilde |
| 127 | 7F | DEL | Delete |
Conversioni in basi diverse
I numeri naturali maggiori o uguali alla base β possono essere rappresentati in maniera unica da una sequenza di cifre secondo la rappresentazione posizionale.
Se un numero naturale N ≥ β è rappresentato in base β dalla sequenza di cifre:
ap-1 ap-2 ... a1 a0
allora N può essere espresso tramite la "formula della sommatoria":
N = Σ ( ai · βi )
= ap-1·βp-1 + ap-2·βp-2 + ... + a1·β1 + a0·β0
(con i che va da 0 a p-1)
Teorema Fondamentale della Rappresentazione dei Numeri Naturali
Il fatto che, dato un naturale N, esista e sia unica la sequenza ap-1...a0 (avendo rimosso eventuali zeri all’inizio) è garantito da questo teorema.
- a0 è detta "cifra meno significativa" (LSB - Least Significant Bit/Digit).
- ap-1 è detta "cifra più significativa" (MSB - Most Significant Bit/Digit).
Conversione Binario → Decimale (Somma Pesata)
Per convertire da base 2 a base 10, si moltiplica ogni cifra binaria per la potenza di 2 corrispondente alla sua posizione (partendo da 0 a destra).
Esempio 1: Convertire 101102
= 1·24 + 0·23 + 1·22 + 1·21 + 0·20
= 16 + 0 + 4 + 2 + 0
= 2210
Esempio 2: Convertire 110012
= 1·24 + 1·23 + 0·22 + 0·21 + 1·20
= 16 + 8 + 0 + 0 + 1
= 2510
Conversione Decimale → Binario (Divisioni Successive)
Per convertire un numero decimale in binario, si divide ripetutamente il numero per 2 e si prendono i resti in ordine inverso (dall'ultimo al primo).
Esempio 1: Convertire 1310
13 / 2 = 6 con resto 1 ^
6 / 2 = 3 con resto 0 |
3 / 2 = 1 con resto 1 | Leggi i resti
1 / 2 = 0 con resto 1 | dal basso verso l'alto
Risultato: 11012
Esempio 2: Convertire 4210
42 / 2 = 21 con resto 0 ^
21 / 2 = 10 con resto 1 |
10 / 2 = 5 con resto 0 |
5 / 2 = 2 con resto 1 |
2 / 2 = 1 con resto 0 |
1 / 2 = 0 con resto 1 |
Risultato: 1010102
L'Overflow (Traboccamento)
Il calcolatore lavora con un numero finito di bit (fissato dall'hardware o dal tipo di dato, es. 16 bit, 32 bit). Quando il risultato di un'operazione supera il valore massimo rappresentabile con quei bit, si verifica un Overflow.
L'analogia del Contachilometri:
Immagina un contachilometri a 3 cifre (Max 999). Se sei a 999 e fai un altro km, non vai a 1000, ma torni a 000. La cifra delle migliaia "trabocca" e viene persa. Questo è l'overflow.
Esempio pratico (con 16 bit)
Supponiamo di avere p = 16 bit. Il valore massimo rappresentabile (senza segno) è 65.535 ( 216-1 ).
A = 30421 (0111011011010101)
+ B = 43127 (1010100001110111)
------------------------------
TOT = 73548
Il risultato reale (73548) è maggiore del massimo consentito (65535).
In binario, 73548 richiede 17 bit:
1
0001111101001100
↑ Bit di riporto (perso) ↑ Parte salvata (16 bit)
Il 17° bit viene "perso". Il computer memorizza solo i 16 bit rimanenti, quindi il risultato sembrerà essere 8012 invece di 73548. Un errore gravissimo!
Regola Generale: Per rappresentare sicuramente la somma di due numeri a p bit, sono necessari p+1 bit.
Aritmetica Binaria
Le operazioni nel sistema binario funzionano in modo analogo a quello decimale, ma sono più semplici poiché ci sono solo due cifre (0 e 1).
Somma Binaria
Si mettono i numeri in colonna allineandoli a destra. Si sommano le cifre colonna per colonna, partendo da destra, tenendo conto dei riporti.
Regole di Somma:
0 + 0 = 00 + 1 = 11 + 0 = 11 + 1 = 0con riporto di 1 (che va sommato alla colonna successiva)1 + 1 + 1 = 1con riporto di 1 (cifra + riporto precedente)
Esempio: 101100 + 111010
1 1 1 ← Riporti
1 0 1 1 0 0 +
1 1 1 0 1 0 =
-------------
1 1 0 0 1 1 0
Sottrazione Binaria
Anche qui si allinea a destra. Se la cifra superiore è minore di quella inferiore (0 - 1), si prende in prestito una unità dalla colonna a sinistra.
Regole di Sottrazione:
0 - 0 = 01 - 1 = 01 - 0 = 10 - 1 = 1con prestito di 1 dalla colonna a sinistra (il prestito vale 2 nella colonna corrente, quindi 2-1=1)
Esempio: 11001 - 01011
0 1 1 0 ← Prestiti (il 1 diventa 0, lo 0 diventa 10 cioè 2)
1 1 0 0 1 -
0 1 0 1 1 =
-----------
0 1 1 1 0
Moltiplicazione Binaria
È molto più semplice di quella decimale perché le cifre sono solo 0 o 1. Non servono tabelline: o si copia il numero (se x1) o si scrive zero (se x0).
Regole:
0 × 0 = 00 × 1 = 01 × 0 = 01 × 1 = 1
Procedimento: Si moltiplica il primo fattore per ogni cifra del secondo, scalando (shift) verso sinistra ogni volta, e infine si sommano i risultati parziali.
Esempio: 1011 × 101
1 0 1 1 ×
1 0 1 =
-------------
1 0 1 1 (1011 × 1)
0 0 0 0 - (1011 × 0) - Shift a sx
1 0 1 1 - - (1011 × 1) - Shift a sx
-------------
1 1 0 1 1 1 (Somma finale)
Divisione Binaria
Simile alla divisione in colonna decimale (Euclidea). Si abbassano le cifre del dividendo e si controlla se il divisore "ci sta".
In binario è facile: il divisore "ci sta" solo 1 volta o 0 volte.
Esempio: 11011 : 11 (27 : 3 = 9)
1 1 0 1 1 | 1 1 ← Divisore
1 1 |-------
--- | 1 0 0 1 ← Quoziente (Risultato)
0 0 0 |
0 | (11 in 0 non ci sta -> scrivo 0)
--- |
0 1 | (abbasso 1)
0 | (11 in 1 non ci sta -> scrivo 0)
--- |
1 1 | (abbasso 1)
1 1 | (11 in 11 ci sta 1 volta -> scrivo 1)
---
0 ← Resto
Rappresentazione in Modulo e Segno
È il metodo più intuitivo per rappresentare numeri interi con segno. Il bit più a sinistra (MSB) viene usato per il segno, mentre gli altri rappresentano il valore assoluto.
Dato un numero di bit p, la rappresentazione A è data da:
A = ap-1 ... a0 = ( Segno, Modulo )
- Segno (ap-1): 0 se positivo , 1 se negativo .
- Modulo : I restanti p-1 bit rappresentano il valore assoluto.
Esempi (con p = 4 bit)
a = +3
Segno (0) | Modulo (3 -> 011)
Risultato: 0011
a = -3
Segno (1) | Modulo (3 -> 011)
Risultato: 1011
Decodifica (da Binario a Decimale)
Per convertire una sequenza A = (a(p-1), a(p-2)...a(0)) in intero:
a = (ap-1 == 0) ? +a : -a
Il Problema del Doppio Zero:
In questa codifica lo zero ha due rappresentazioni:
- +0:
000...0(es. 0000) - -0:
100...0(es. 1000)
Questo è uno svantaggio perché complica i circuiti di confronto.
Tabella dei Valori (p = 4 bit)
Range rappresentabile: da -(2p-1 - 1) a +(2p-1 - 1) (es. da -7 a +7).
| Binario | Valore | Binario | Valore |
|---|---|---|---|
| 0000 | +0 | 1000 | -0 |
| 0001 | +1 | 1001 | -1 |
| 0010 | +2 | 1010 | -2 |
| 0011 | +3 | 1011 | -3 |
| 0100 | +4 | 1100 | -4 |
| 0101 | +5 | 1101 | -5 |
| 0110 | +6 | 1110 | -6 |
| 0111 | +7 | 1111 | -7 |
Rappresentazione in Complemento a 2
Perché i calcolatori utilizzano il CA2?
Il Complemento a due è diventato lo standard universale per tre ragioni fondamentali che ottimizzano l'architettura della CPU:
- Unicità dello Zero: A differenza della rappresentazione in Modulo e Segno (che genera sia +0 che -0), nel CA2 esiste una sola sequenza di bit per lo zero. Questo elimina ambiguità logiche e semplifica drasticamente i circuiti di confronto.
- Semplificazione dell'Hardware (L'Addizionatore): Il vantaggio principale è economico e progettuale: non serve un circuito dedicato alla sottrazione. La CPU calcola A - B come A + (-B). Poiché l'operazione per rendere negativo un numero è quasi istantanea a livello di transistor, il computer usa lo stesso identico componente (ALU Addizionatore) per entrambe le operazioni.
- Gestione coerente dei pesi: Il bit più a sinistra (MSB) continua a indicare il segno (0 per positivo, 1 per negativo), ma assume un valore matematico pari a -2n-1. Questo permette di trattare tutti i bit con una logica aritmetica uniforme.
| Sistema | Vantaggi | Svantaggi |
|---|---|---|
| Modulo e Segno | Intuitivo | Doppio zero; circuiti complessi |
| Comp. a 2 | Zero unico; solo addizioni | Meno leggibile per umani |
1. Codifica (da intero a a bit A)
Dato un numero intero a e un numero di bit p:
- Se a ≥ 0: La rappresentazione A è il numero naturale corrispondente ad a (su p bit).
- Se a < 0: La rappresentazione A è il numero naturale corrispondente a 2p - |a|.
Esempi (con p = 4 bit, 24 = 16):
a = +7
Poiché è positivo -> Binario di 7 su 4 bit
A = 0111
a = -1
Poiché è negativo -> Calcolo 16 - |-1| = 15
Binario di 15 su 4 bit
A = 1111
a = -8
Poiché è negativo -> Calcolo 16 - |-8| = 8
Binario di 8 su 4 bit
A = 1000
Metodo Rapido (Inversione bit):
Per trovare il complemento a 2 di un numero negativo (es. -3):
1. Scrivi il positivo (+3) in binario: 0011
2. Inverti tutti i bit: 1100
3. Somma 1: 1101 (Ecco -3!)
2. Decodifica (da bit A a intero a)
Per capire che numero rappresenta una sequenza di bit:
- Se il bit più significativo (ap-1) è 0: Il numero è positivo e vale semplicemente il valore binario.
- Se il bit più significativo (ap-1) è 1: Il numero è negativo. Il suo valore è -(2p - A), oppure si applica il peso negativo al bit più alto (-2p-1).
A = 0111 (MSB è 0)
Valore = +7
A = 1111 (MSB è 1, quindi negativo)
Valore A come unsigned = 15
a = -(16 - 15) = -1
Tabella dei Valori (p = 4 bit)
Notare che esiste un solo zero e il range è asimmetrico: [-8, +7].
| Binario (A) | Valore (a) | Binario (A) | Valore (a) |
|---|---|---|---|
| 0000 | 0 | 1000 | -8 |
| 0001 | +1 | 1001 | -7 |
| 0010 | +2 | 1010 | -6 |
| 0011 | +3 | 1011 | -5 |
| 0100 | +4 | 1100 | -4 |
| 0101 | +5 | 1101 | -3 |
| 0110 | +6 | 1110 | -2 |
| 0111 | +7 | 1111 | -1 |
Vantaggi:
- Non viene sprecata nessuna combinazione per il "-0" (lo zero è unico: 0000).
- I numeri negativi hanno sempre il bit più significativo a 1.
- Le operazioni di somma e sottrazione funzionano con lo stesso circuito hardware.
Rappresentazione con Bias (o Eccesso K)
In questa rappresentazione, il range dei numeri interi viene "traslato" (spostato) aggiungendo una costante positiva detta Bias (o Eccesso), in modo da ottenere sempre un numero non negativo.
È molto utilizzata per rappresentare gli esponenti nei numeri reali (standard IEEE 754).
Dato un numero di bit p, il Bias si calcola solitamente come:
Bias = 2p-1 - 1
1. Codifica (da a ad A):
A = a + Bias
2. Decodifica (da A ad a):
a = A - Bias
Esempi (con p = 4 bit):
Il Bias vale 24-1 - 1 = 23 - 1 = 7.
a = 0 => A = 0 + 7 = 7 (0111)
a = 1 => A = 1 + 7 = 8 (1000)
a = -1 => A = -1 + 7 = 6 (0110)
a = -7 => A = -7 + 7 = 0 (0000)
a = 8 => A = 8 + 7 = 15 (1111)
Tabella dei Valori (p = 4 bit, Bias = 7)
L'intervallo rappresentabile è [-Bias, Bias + 1], ovvero [-7, +8].
| Binario (A) | Valore Unsigned | Calcolo (A - Bias) | Valore Reale (a) |
|---|---|---|---|
| 0000 | 0 | 0 - 7 | -7 |
| 0001 | 1 | 1 - 7 | -6 |
| ... | ... | ... | ... |
| 0111 | 7 | 7 - 7 | 0 |
| 1000 | 8 | 8 - 7 | +1 |
| ... | ... | ... | ... |
| 1111 | 15 | 15 - 7 | +8 |
Osservazioni Importanti:
- Unico Zero: Lo zero è rappresentato una sola volta (es. 0111 per p=4).
- Bit di Segno "Invertito": Rispetto al complemento a 2, qui i numeri che iniziano con 1 sono i più grandi (positivi), mentre quelli che iniziano con 0 sono i più piccoli (negativi o zero).
- Overflow: Prima di convertire, bisogna verificare che il numero sia rappresentabile.
Esempio: Se voglio rappresentare -9 su 4 bit (Bias 7): A = -9 + 7 = -2. Il risultato non è un numero naturale rappresentabile (è negativo), quindi è Overflow.
Rappresentazione Numeri Reali
Virgola Fissa
In questa rappresentazione, si utilizza un numero fisso di bit per la parte intera e un numero fisso di bit per la parte frazionaria.
Dati p bit totali, di cui f riservati alla parte frazionaria:
- Parte Intera: usa i primi p-f bit.
- Parte Frazionaria: usa gli ultimi f bit.
La virgola è "implicita" e non viene memorizzata fisicamente.
Algoritmo di Conversione
Il numero reale viene diviso in due parti: I (intera) e F (frazionaria).
- Parte Intera: Si converte con il metodo standard (divisioni successive per 2).
- Parte Frazionaria: Si usa il metodo delle moltiplicazioni successive:
- Si prende la parte frazionaria e la si moltiplica per 2.
- La parte intera del risultato diventa il bit (0 o 1).
- Si ripete l'operazione sulla nuova parte frazionaria ottenuta.
- Ci si ferma quando la parte frazionaria è 0 o si sono finiti i bit f disponibili.
Esempio 1: Conversione Esatta
Convertire 331.6875 (con p=16 e f=5).
1. PARTE INTERA (331):
331 in binario = 101001011 (9 bit)
2. PARTE FRAZIONARIA (0.6875):
0.6875 * 2 = 1.375 -> Bit: 1
0.375 * 2 = 0.75 -> Bit: 0
0.75 * 2 = 1.5 -> Bit: 1
0.5 * 2 = 1.0 -> Bit: 1
(Resto 0, fine)
Binario Frazionario = 1011
3. UNIONE (su 16 bit totali, f=5):
Intero: 00101001011 (padding a 11 bit)
Frazion.: 10110 (padding a 5 bit)
Risultato: 00101001011.10110
Esempio 2: Errore di Troncamento (Periodicità)
Convertire 2.3 (con f=6).
Alcuni numeri decimali finiti possono diventare periodici in binario, richiedendo infiniti bit.
1. PARTE INTERA (2):
2 in binario = 10
2. PARTE FRAZIONARIA (0.3):
0.3 * 2 = 0.6 -> Bit: 0
0.6 * 2 = 1.2 -> Bit: 1
0.2 * 2 = 0.4 -> Bit: 0
0.4 * 2 = 0.8 -> Bit: 0
0.8 * 2 = 1.6 -> Bit: 1
0.6 * 2 = 1.2 -> Bit: 1
... (si ripete la sequenza 1001) ...
Binario (infinito): 0.010011001...
Errore di Troncamento:
Avendo solo f=6 bit, dobbiamo tagliare la sequenza: 0.010011.
Il valore salvato sarà 2.296875 invece di 2.3. La differenza è l'errore di troncamento.
Numeri Reali: Virgola Mobile (IEEE 754)
Lo standard IEEE 754 definisce come rappresentare i numeri reali nel calcolatore usando la notazione scientifica normalizzata in base 2.
Un numero reale è rappresentato dalla tripla: (Segno, Esponente, Mantissa).
Il valore reale r si calcola come:
r = (-1)s × (1 + f) × 2e
- s (Segno): 1 bit (0 = positivo, 1 = negativo).
- f (Mantissa): Parte frazionaria normalizzata (si assume un "1." implicito davanti).
- e (Esponente reale): Calcolato sottraendo il Bias all'esponente memorizzato E (
e = E - Bias).
Formati Standard
| Formato | Bit Totali | Esp. (K) | Mantissa (G) | Bias | Range Approx. |
|---|---|---|---|---|---|
| Half Precision | 16 | 5 | 10 | 15 | 10-5 ... 105 |
| Single Precision (float) | 32 | 8 | 23 | 127 | 10-38 ... 1038 |
| Double Precision (double) | 64 | 11 | 52 | 1023 | 10-308 ... 10308 |
Esempi (Half Precision)
Consideriamo 16 bit: 1 segno, 5 esponente (Bias=15), 10 mantissa.
1. Decodifica (da Binario a Reale)
R = 0 01111 0000000001
Segno (s): 0 -> Positivo (+)
Esponente (E): 01111 (15) -> e = 15 - 15 = 0
Mantissa (F): ...001 -> f = 2^-10 (circa 0.00097)
Calcolo:
r = +1.00097 * 2^0 = +1.00097
2. Codifica (da Reale a Binario)
Convertire r = 2.3 in Half Precision.
1. Virgola Fissa:
2.3 = 10.010011001... (periodico)
2. Normalizzazione (sposto virgola dopo il primo 1):
1.0010011001... * 2^1
3. Calcolo Esponente E:
e = 1
E = e + Bias = 1 + 15 = 16 -> 10000
4. Mantissa F (prendo 10 bit dopo la virgola):
0010011001
Risultato R:
0 10000 0010011001
Nota: L' "1." iniziale (parte intera normalizzata) non viene memorizzato per risparmiare bit. È detto Hidden Bit.