RAPPRESENTAZIONE DELL’INFORMAZIONE
Internamente a un elaboratore, ogni
informazione è rappresentata tramite
sequenze di bit (cifre binarie)
Una sequenza di bit non dice “cosa”
essa rappresenta: l’interpretazione è
negli occhi di chi guarda
Ad esempio, 01000001 può rappresentare:
l’intero 65, il carattere ‘A’, il boolean ‘vero’, …
… il valore di un segnale musicale,
… il colore di un puntino sullo schermo...
INFORMAZIONI NUMERICHE
La rappresentazione delle informazioni
numeriche è di particolare rilevanza
In particolare vogliamo poter trattare:
numeri naturali (interi senza segno)
numeri interi (con segno)
numeri reali
con la consapevolezza di:
eventuali limiti nella loro rappresentazione
e nel loro uso
eventuali approssimazioni necessarie.
NUMERI NATURALI (interi senza segno)
Dominio:
N = { 0,1,2,3, …}
Rappresentabili con diverse notazioni
non posizionali
ad esempio la notazione romana:
I, II, III, IV, V, .... IX, X, XI...
posizionale
1, 2, .. 10, 11, ... 200, ...
NUMERI NATURALI (interi senza segno)
Non posizionali: hanno regole proprie,
spesso assai
Dominio: che rendono
N = { 0,1,2,3,
…} complessa
l'esecuzione dei calcoli
Rappresentabili con diverse notazioni
non posizionali
ad esempio la notazione romana:
I, II, III, IV, V, .... IX, X, XI...
posizionale
1, 2, .. 10, 11, ... 200, ...
Posizionale: rappresenta i numeri in
modo compatto, e rende semplice l'effettuazione dei calcoli
NOTAZIONE POSIZIONALE
Concetto di base di rappresentazione B
Rappresentazione del numero come
sequenza di simboli (cifre) appartenenti
a un alfabeto di B simboli distinti
ogni simbolo rappresenta un valore
compreso fra 0 e B-1
Esempio (base B = tre, alfabeto A = {$,%,#}):
$ rappresenta il valore zero
% rappresenta il valore uno
# rappresenta il valore due
NOTAZIONE POSIZIONALE
Il valore di un numero espresso in questa
notazione è ricavabile
a partire dal valore rappresentato da ogni
simbolo
pesandolo in base alla posizione che occupa
nella sequenza
dn-1 … d2 d1 d0
Posizione n-1:
pesa Bn-1
Posizione 1:
pesa B1
Posizione 0:
pesa B0 (unità)
NOTAZIONE POSIZIONALE
Il valore di un numero espresso in questa
notazione è ricavabile
a partire dal valore rappresentato da ogni
simbolo
pesandolo in base alla posizione che occupa
nella sequenza
Esempio ($ indica zero, % indica uno, # indica due):
%## rappresenta il valore diciassette
%$% rappresenta il valore dieci
NOTAZIONE POSIZIONALE
Il valore v di un numero espresso in questa
notazione è ricavabile
a partire dal valore rappresentato da ogni
simbolo
pesandolo in base alla posizione che occupa
nella sequenza
In formula:
n1
v dk B
k
k 0
B = base, dk = cifre (ognuna rappresenta un valore fra 0 e B-1)
NOTAZIONE POSIZIONALE
Quindi, una sequenza di cifre (stringa)
non è interpretabile se non si precisa la
base in cui è espressa
Esempi:
Stringa
“12”
“12”
“12”
“12”
Base
quattro
otto
dieci
sedici
Alfabeto
{0,1,2,3}
{0,1,...,7}
{0,1,...,9}
{0,..,9, A,., F}
Calcolo valore
4*1+2
8*1+2
10 * 1 + 2
16 * 1 + 2
Valore
sei
dieci
dodici
diciotto
NOTAZIONE POSIZIONALE
Ogni numero può essere espresso, in
modo univoco, in una qualunque base
Esempi:
Numero
venti
venti
venti
venti
Base
due
otto
dieci
sedici
Alfabeto
Rappresentazione
{0,1}
“10100”
{0,1,...,7}
“24”
{0,1,...,9}
“20”
{0,..,9, A,., F}
“14”
CONVERSIONI stringa/numero/stringa
Ogni numero può essere espresso, in
modo univoco, in una qualunque base
Quindi, deve essere possibile:
data la rappresentazione di un numero in
una certa base, determinare il valore del
numero
dato un numero, determinare la sua
rappresentazione in una certa base
CONVERSIONI stringa/numero/stringa
Ogni numero può essere espresso, in
modo univoco, in una qualunque base
Quindi, deve
essere possibile:
Conversione
da stringa a numero
data la rappresentazione di un numero in
una certa base, determinare il valore del
numero
dato un numero, determinare la sua
rappresentazione in una certa base
Conversione da numero a stringa
CONVERSIONE STRINGA / NUMERO
Problema: data la rappresentazione di un
numero in una certa base, determinare il
valore del numero
Soluzione: applicare la formula
n1
v dk B
k
k 0
Stringa
“10100”
“12”
“1A”
Base
due
dieci
sedici
Alfabeto
Calcolo valore
Valore
{0,1}
1 * 2 4 + 1 * 22
venti
{0,1,...,9}
1 * 101 + 2 * 100 dodici
{0,..,9, A,., F} 1 * 161 + 10 * 160 ventisei
CONVERSIONE NUMERO / STRINGA
Problema: dato un numero, determinare
la sua rappresentazione
Soluzione: dipende se la rappresentazione scelta è posizionale o meno
se non è posizionale, le regole dipendono dalla
specifica rappresentazione
ad esempio, ventisette in notazione romana:
27 è compreso fra 20 e 30 accumulo 20 XX
27-20 = 7 è compreso fra 5 e 10 accumulo 5 V
7-5 = 2 si esprime direttamente II
morale: XXVII
CONVERSIONE NUMERO / STRINGA
Problema: dato un numero, determinare
la sua rappresentazione in una base data
Soluzione (notazione posizionale):
manipolare la formula per dedurre un
algoritmo
n1
v dk B
k
v è noto,
le cifre dk vanno calcolate
k 0
= d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... )))
CONVERSIONE NUMERO / STRINGA
n1
v dk B
k
k 0
= d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... )))
d0 si può ricavare come resto della divisione
intera v / B
tale divisione ha per quoziente
q = d1 + B * ( d2 + B * ( d3 + ... )), che consente
di trovare le altre cifre iterando il procedimento
NB: le cifre vengono prodotte nell'ordine dalla meno significativa (LSB) alla più significativa (MSB)
CONVERSIONE NUMERO / STRINGA
n1
v dk B
k
k0
= d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... )))
Algoritmo delle divisioni successive
d0 si può ricavare come resto della divisione
intera v / B
tale divisione ha per quoziente
q = d1 + B * ( d2 + B * ( d3 + ... )), che consente
di trovare le altre cifre iterando il procedimento
NB: le cifre vengono prodotte nell'ordine dalla meno significativa (LSB) alla più significativa (MSB)
CONVERSIONE NUMERO / STRINGA
Algoritmo delle divisioni successive
si divide v per B
il resto costituisce la cifra meno significativa
il quoziente serve a iterare il procedimento
se tale quoziente è zero, l’algoritmo
termina;
se non lo è, lo si assume come nuovo
valore v’, e si itera il procedimento
con il valore v’.
CONVERSIONE NUMERO / STRINGA
Esempi
Numero
quattordici
undici
sessantatre
sessantatre
Base
4
2
10
16
Calcolo valore
14 / 4 = 3 con resto 2
3 / 4 = 0 con resto 3
11 / 2 = 5 con resto 1
5 / 2 = 2 con resto 1
2 / 2 = 1 con resto 0
1 / 2 = 0 con resto 1
63 / 10 = 6 con resto 3
6 / 10 = 0 con resto 6
63 / 16 = 3 con resto 15
3 / 16 = 0 con resto 3
Stringa
“32”
“1011”
“63”
“3F”
RAPPRESENTAZIONI IN BASI DIVERSE
In generale, le rappresentazioni di uno
stesso numero in basi diverse non sono
correlate fra loro
Esempio: il numero sessantasette
B1 = 2 “01000011”
B2 = 8 “103”
B3 = 10 “67”
B4 = 16 “43”
RAPPRESENTAZIONI IN BASI DIVERSE
Tuttavia, diventano correlate se le basi
considerate sono una potenza una
dell’altra:
B2 = (B1 )N
Allora,
N cifre nella rappresentazione in base B1
corrispondono esattamente a 1 cifra
nella rappresentazione in base B2
RAPPRESENTAZIONI IN BASI DIVERSE
Tuttavia, diventano correlate se le basi
considerate sono una potenza una
dell’altra
8 = 23
Esempio:
1 cifra ottale = 3 cifre binarie
B1 = 2 “01000011”
B2 = 8 = 23 = (B1)3 “103”
01.000.011
B3 = 10 “67”
B4 = 16 = 24 = (B1)4 “43”
0100.0011
16 = 24
1 cifra hex = 4 cifre binarie
RAPPRESENTAZIONI IN BASI DIVERSE
Conseguenza: se le basi considerate
sono una potenza una dell’altra,
per passare dalla rappresentazione di un
numero in una base B1 alla sua rappresentazione in un’altra base B2 = (B1 )N
basta sostituire ordinatamente N cifre
della rappresentazione B1 con 1 cifra
della rappresentazione B2
RAPPRESENTAZIONI IN BASI DIVERSE
Numero
uno
due
tre
quattro
cinque
otto
dieci
quindici
sedici
trentuno
trentadue
cento
duecentocinquantacinque
Rappr. Base 2
1
10
11
100
101
1000
1010
1111
10000
11111
100000
1100100
11111111
Rappr. Base 8
1
2
3
4
5
10
12
17
20
37
40
144
377
Rappr. Base 16
1
2
3
4
5
8
A
F
10
1F
20
64
FF
OPERAZIONI IN NOTAZIONE POSIZIONALE
Tutte le notazioni posizionali usano le
stesse regole per le operazioni, indipendentemente dalla base adottata
Esempi di somme e sottrazioni:
15 + 0000 1111 + 0F +
21 = 0001 0101 = 15 =
------------------------36
0010 0100
24
36 - 0010 0100 - 24 21 = 0001 0101 = 15 =
-----------------------15
0000 1111
0F
OPERAZIONI IN NOTAZIONE POSIZIONALE
In moltiplicazioni e divisioni:
spostando tutte le cifre a sinistra di una
posizione (e introducendo uno 0 a destra)
si moltiplica per la base
spostando tutte le cifre a destra di una
posizione (e introducendo uno 0 a sinistra)
si divide per la base
Esempi:
base dieci:
base dieci:
base due:
base due:
184 *
1832 /
1011 *
1111 /
10
10
2
2
= 1840
=
183 _
= 10110
=
111 _
Divisione
intera
OPERAZIONI IN NOTAZIONE POSIZIONALE
Due mosse elementari:
Shift Left (SHL)
15 * 4 =
0000 1111 SHL 2 =
----------------------------60
0011 1100
Shift Right (SHR)
25 / 8 =
0001 1001 SHR 3 =
----------------------------3
0000 0011
Qualunque moltiplicazione o divisione può essere
espressa con somme, sottrazioni e SHL / SHR.
ERRORI NELLE OPERAZIONI
In matematica, le operazioni sui naturali non
danno mai luogo a errori
(posto che la divisione è una divisione intera,
che può comportare l’esistenza di un resto)
In un elaboratore, invece, si possono
generare errori, a causa dell’impossibilità
di rappresentare tutti gli infiniti numeri
in particolare, con N bit il massimo
numero rappresentabile è 2N-1
qualunque operazione che implichi un
risultato maggiore sarà errata OVERFLOW
ERRORI NELLE OPERAZIONI
Teoricamente
decimale
176 +
84 =
---260
binario
1011 0000+
0101 0100=
---------1 0000 0100
Praticamente
decimale
176 +
84 =
---004
binario
1011 0000+
0101 0100=
---------0000 0100
il risultato è completamente errato,
perché è andato perso proprio il
contributo più significativo (MSB)
Soluzione: usare un tipo di dato che
offra un maggior numero di bit
IMPLEMENTARE GLI ALGORITMI
Conversione da stringa a numero
si applica la formula
n1
v dk B
k 0
k
le cifre dk sono note,
il valore v va calcolato
= d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... )))
richiede la valutazione di un
polinomio Metodo di Horner
IMPLEMENTARE GLI ALGORITMI
Conversione da stringa a numero
una funzione: sToNum()
in ingresso:
base b
stringa di simboli, s
lunghezza della stringa, len
in uscita:
il valore del numero (intero senza segno)
IMPLEMENTARE GLI ALGORITMI
unsigned long sToNum(
unsigned short b,
char s[],
int len) {
<finché ci sono simboli in s>
<calcola il valore del simbolo s[i]>
<aggiorna il valore v>
<alla fine, v rappresenta il valore cercato>
}
IMPLEMENTARE GLI ALGORITMI
unsigned long sToNum(
unsigned short b,
char s[],
int len) {
unsigned long v = 0;
<finché ci sono simboli in s>
<calcola il valore del simbolo s[i]>
<aggiorna il valore v>
<alla fine, v rappresenta il valore cercato>
}
IMPLEMENTARE GLI ALGORITMI
unsigned long sToNum(
unsigned short b,
char s[],
int len) {
unsigned long v = 0; int i = 0;
for(i=0; i<len; i++) {
<calcola il valore del simbolo s[i]>
<aggiorna il valore v>
}
<alla fine, v rappresenta il valore cercato>
}
IMPLEMENTARE GLI ALGORITMI
unsigned long sToNum(
unsigned short b,
char s[],
int len) {
unsigned long v = 0; int i = 0;
for(i=0; i<len; i++) {
val = valoreCifra(s[i]);
v = b * v + val; /* Horner */
}
<alla fine, v rappresenta il valore cercato>
}
IMPLEMENTARE GLI ALGORITMI
unsigned long sToNum(
unsigned short b,
char s[],
int len) {
unsigned long v = 0; int i = 0;
for(i=0; i<len; i++) {
v = b * v + valoreCifra(s[i]);;
}
return v;
valoreCifra(),
}
chi era costei ?
IMPLEMENTARE GLI ALGORITMI
unsigned valoreCifra(char ch) {
come fare per calcolare il valore?
}
il carattere è rappresentato internamente da un
numero, secondo la codifica ASCII
è garantito che i caratteri da ‘0’ a ‘9’ sono in
sequenza: quindi,
se ‘0’ è rappresentato internamente dal numero
‘1’ deve essere rappresentato dal numero +1,
‘2’ deve essere rappresentato dal numero +2,
…
‘9’ deve essere rappresentato dal numero +9
IMPLEMENTARE GLI ALGORITMI
unsigned valoreCifra(char ch) {
come fare per calcolare il valore?
}
conseguenza: la differenza tra un carattere
numerico (compreso fra ‘0’ e ‘9’) e il carattere
‘0’ è proprio il valore del simbolo stesso!
‘0’ -’0’ = - = 0
‘1’ -’0’ = (+1) - = 1
‘2’ -’0’ = (+2) - = 2
…
‘9’ -’0’ = (+9) - = 9
IMPLEMENTARE GLI ALGORITMI
unsigned valoreCifra(char ch) {
come fare per calcolare il valore?
}
lo stesso approccio vale per le lettere da ‘A’ a
‘F’ che rappresentano i valori da 10 a 15 nel
caso della base sedici (esadecimale); quindi,
se ‘A’ è rappresentato internamente dal numero
‘B’ deve essere rappresentato dal numero +1,
‘C’ deve essere rappresentato dal numero +2,
…
‘Z’ deve essere rappresentato dal numero +25
IMPLEMENTARE GLI ALGORITMI
unsigned valoreCifra(char ch) {
come fare per calcolare il valore?
}
e anche per le minuscole da ‘a’ a ‘f’, che pure
rappresentano i valori da 10 a 15:
se ‘a’ è rappresentato internamente dal numero
‘b’ deve essere rappresentato dal numero +1,
‘c’ deve essere rappresentato dal numero +2,
…
‘z’ deve essere rappresentato dal numero +25
IMPLEMENTARE GLI ALGORITMI
unsigned valoreCifra(char ch) {
come fare per calcolare il valore?
}
conseguenza: la differenza tra un carattere
alfabetico compreso fra ‘A’ e ‘F’ (o fra ‘a’ e ‘f’) e
il carattere ‘A’ (oppure, rispettivamente, ‘a’)
consente di trovare il valore corrispondente
‘A’ -’A’ = - = 0 sommando 10 ottengo 10
‘B’ -’A’ = ( +1) - = 1 sommando 10 ottengo 11
‘C’ -’A’ = ( +2) - = 2 sommando 10 ottengo 12
…
‘F’ -’A’ = ( +5) - = 5 sommando 10 ottengo 15
IMPLEMENTARE GLI ALGORITMI
unsigned valoreCifra(char
return
('0'<= ch)&&(ch <= '9')
ch - '0' :
('a'<= ch)&&(ch <= 'f')
ch - 'a' + 10 :
('A'<= ch)&&(ch <= 'F')
ch - 'A' + 10 : BOH;
}
ch) {
?
?
?
Qui la funzione è indeterminata
(questo caso non dovrebbe mai verificarsi)
IMPLEMENTARE GLI ALGORITMI
un approccio ricorsivo
unsigned long sToNum(
unsigned short b,
char s[], int len) {
<se len=0, il valore è 0>
<se len=1, il valore è val(s[0])>
<in ogni altro caso, il valore è
val(s[len-1]) + b * sToNum(b,s,len-1) >
}
IMPLEMENTARE GLI ALGORITMI
un approccio ricorsivo
unsigned long sToNum(
unsigned short b,
char s[], int len) {
if (len==0) return 0;
else if (len==1)
return valoreCifra(s[0]);
else return
valoreCifra(s[len-1]) +
b * sToNum(b,s,len-1) ;
}
IMPLEMENTARE GLI ALGORITMI
Esempio: un cliente
main(){
char st[] = "367";
unsigned long val8 = sToNum(8, st,3);
unsigned long val10 = sToNum(10,st,3);
unsigned long val16 = sToNum(16,st,3);
/* quanto valgono le variabili? */
}
Provare con entrambe le versioni di sToNum()
CONVERSIONE NUMERO / STRINGA
Problema: dato un numero, determinare
la sua rappresentazione in una base data
Soluzione (notazione posizionale):
manipolare la formula per dedurre un
algoritmo
n1
v dk B
k
v è noto,
le cifre dk vanno calcolate
k 0
= d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... )))
IMPLEMENTARE GLI ALGORITMI
Algoritmo delle divisioni successive
si divide v per B
il resto costituisce la cifra meno significativa
il quoziente serve a iterare il procedimento
se tale quoziente è zero, l’algoritmo
termina;
se non lo è, lo si assume come nuovo
valore v’, e si itera il procedimento
con il valore v’.
IMPLEMENTARE GLI ALGORITMI
Conversione da numero a stringa
una funzione: numToS()
in ingresso:
base b
numero n
in uscita:
la passiamo come parametro
(passa per riferimento)
ipotesi: inizialmente è vuota
ed è sufficientemente lunga
stringa di simboli, s
(lunghezza della stringa)
implicita nella stringa
IMPLEMENTARE GLI ALGORITMI
void numToS(unsigned short b,
unsigned long v, char s[]) {
<ripeti>
<calcola il resto v%B>
<calcola il carattere corrispondente e
inseriscilo in testa alla stringa>
<sostituisci a v il nuovo valore v/B >
<per tutto il tempo che v>0 >
}
IMPLEMENTARE GLI ALGORITMI
void numToS(unsigned short b,
unsigned long
v,scrivere
charuna
s[])
{
occorre
funzione
char convertiCifra(unsigned
<ripeti>
int)
<calcola il resto v%B>
<calcola il carattere corrispondente>
<inseriscilo in testa alla stringa>
<sostituisci a v il nuovo valore v/B >
<per tutto il tempooccorre
che v>0
> una procedura
scrivere
}
void aggiungiInTesta(...)
che lo faccia
IMPLEMENTARE GLI ALGORITMI
void numToS(unsigned short b,
unsigned long v, char s[]) {
do {
<calcola il resto v%B>
<calcola il carattere corrispondente e
inseriscilo in testa alla stringa>
<sostituisci a v il nuovo valore v/B >
} while (v>0);
}
IMPLEMENTARE GLI ALGORITMI
void numToS(unsigned short b,
unsigned long v, char s[]) {
do {
resto = v % b;
<calcola il carattere corrispondente e
inseriscilo in testa alla stringa>
<sostituisci a v il nuovo valore v/B >
} while (v>0);
}
IMPLEMENTARE GLI ALGORITMI
void numToS(unsigned short b,
unsigned long v, char s[]) {
do {
resto = v % b;
ch = convertiCifra(resto);
aggiungiInTesta(ch,s);
<sostituisci a v il nuovo valore v/B >
} while (v>0);
}
IMPLEMENTARE GLI ALGORITMI
void numToS(unsigned short b,
unsigned long v, char s[]) {
do {
resto = v % b;
ch = convertiCifra(resto);
aggiungiInTesta(ch,s);
v = v / b;
} while (v>0);
}
IMPLEMENTARE GLI ALGORITMI
void numToS(unsigned short b,
unsigned long v, char s[]) {
do {
ch = convertiCifra(v % b);
aggiungiInTesta(ch,s);
v = v / b;
} while (v>0);
}
IMPLEMENTARE GLI ALGORITMI
void numToS(unsigned short b,
unsigned long v, char s[]) {
do {
aggiungiInTesta(
convertiCifra(v % b), s);
v = v / b;
} while (v>0);
}
IMPLEMENTARE GLI ALGORITMI
char convertiCifra(unsigned n) {
return
( 0 <= n)&&(n <= 9) ?
n + '0' :
(10 <= n)&&(n <= 15) ?
n - 10 + 'A' : '_';
}
Qui la funzione è indeterminata
(questo caso non dovrebbe mai verificarsi)
IMPLEMENTARE GLI ALGORITMI
void aggiungiInTesta(
char ch, char st[]) {
<sposta tutti i caratteri a destra di una
posizione, per fare posto al nuovo carattere>
<copia il nuovo carattere nella prima posizione della stringa>
}
IMPLEMENTARE GLI ALGORITMI
void aggiungiInTesta(
char ch, char st[]) {
int i;
for(i=strlen(st); i>=0; i--){
st[i+1] = st[i];
}
<copia il nuovo carattere nella prima posizione della stringa>
}
IMPLEMENTARE GLI ALGORITMI
void aggiungiInTesta(
char ch, char st[]) {
int i;
for(i=strlen(st); i>=0; i--)
st[i+1] = st[i];
st[0] = ch;
}
IMPLEMENTARE GLI ALGORITMI
Esempio: un cliente
main(){
char s2[10] = "", s8[5] = "",
s10[5] = "", s16[5] = "";
numToS( 2, 250, s2);
numToS( 8, 250, s8);
numToS(10, 250, s10);
numToS(16, 250, s16);
/* quanto valgono le stringhe? */
}
IMPLEMENTARE GLI ALGORITMI
un approccio ricorsivo
void numToS(unsigned short b,
unsigned long v, char s[]) {
<calcola il carattere corrispondente al
resto v%B e inseriscilo in coda alla
stringa>
<se v=0, ritorna> [altrimenti]
<calcola la stringa corrisp. a v/B>
}
È la ricorsione che “inverte l’ordine”
NUMERI INTERI (con segno)
Dominio:
Z = { …, -2,-1,0,1,2,3, … }
Rappresentare gli interi in un elaboratore pone alcune problematiche:
come rappresentare il “segno meno”?
possibilmente, rendere semplice
l’esecuzione delle operazioni
magari usando gli stessi circuiti
usati per i naturali…?
NUMERI INTERI (con segno)
Due possibilità:
rappresentazione in modulo e segno
semplice e intuitiva…
… ma inefficiente e complessa nella gestione
delle operazioni non molto usata in pratica
rappresentazione in complemento a due
meno intuitiva, costruita “ad hoc”
ma efficiente e capace di rendere semplice la
gestione delle operazioni largamente usata
NUMERI INTERI (con segno)
Rappresentazione in modulo e segno
un bit per rappresentare il segno
0 = +
1 = -
N-1 bit per rappresentare il valore assoluto
Esempi (su 8 bit, MSB rappresenta il segno):
+ 5
=
0 0000101
- 36
=
1 0100100
NUMERI INTERI (con segno)
Rappresentazione in modulo e segno
Difetti:
due diverse rappresentazioni per lo zero
+ 0 = 00000000
- 0 = 10000000
occorrono algoritmi speciali per fare le
operazioni
se si adottano le usuali regole,
non è verificata la proprietà X + (-X) = 0
occorrono regole (e quindi circuiti) ad hoc
NUMERI INTERI (con segno)
+5
Rappresentazione in modulo
-5
--Difetti:
0
0 0000101
e segno
1 0000101
---------1 0001010
due diverse rappresentazioni per lo zero
+ 0 = 00000000 Cos’è
- 0 =questa
10000000
roba???
(+5) + (-5) = -10 ???
occorrono algoritmi speciali per fare le
operazioni
se si adottano le usuali regole,
non è verificata la proprietà X + (-X) = 0
occorrono regole (e quindi circuiti) ad hoc
NUMERI INTERI (con segno)
Rappresentazione in complemento a due
si vogliono poter usare le regole standard
per fare le operazioni
in particolare, si vuole che
X + (-X) = 0
la rappresentazione dello zero sia unica
anche a prezzo di una notazione più
complessa, meno intuitiva, e magari non
(completamente) posizionale !
NUMERI INTERI (con segno)
Rappresentazione in complemento a due
idea: cambiare il peso del bit più
significativo da +2N-1 a -2N-1
il peso degli altri bit rimane intoccato.
Esempi (su 8 bit, MSB ha peso negativo):
0 0000101
=
+5
1 0000101
=
-128 + 5 = - 123
1 1111101
=
-128 + 125 = - 3
NUMERI INTERI (con segno)
Rappresentazione in complemento a due
MSB=0
numero
o nullo
idea: cambiare
il peso
del bitpositivo
più
MSB=1
numero
negativo
N-1
N-1
significativo da +2 a -2
Ma nel secondo caso gli altri bit
il valore intoccato.
assoluto!
il peso degli non
altrisono
bit rimane
Esempi (su 8 bit, MSB ha peso negativo):
0 0000101
=
+5
1 0000101
=
-128 + 5 = - 123
1 1111101
=
-128 + 125 = - 3
NUMERI INTERI (con segno)
Rappresentazione in complemento a due
Intervallo di numeri rappresentabili
se MSB=0, è come per i naturali con N-1 bit
da 0 a 2N-1-1
Esempio: su 8 bit, [0,+127]
se MSB=1, stesso intervallo traslato di -2N-1
da -2N-1 a -1
Esempio: su 8 bit, [-128,-1]
Intervallo globale: [-2N-1 , -2N-1-1]
su 8 bit,
[-128,+127]
su 16 bit,
[-32768,+32767]
NUMERI INTERI (con segno)
Rappresentazione
complemento a due
Lo stessoinintervallo
N-1]
prima
era
tutto
sui
positivi
[0...2
Intervallo di numeri rappresentabili
ora è metà sui positivi e metà sui
se MSB=0,negativi
è come[- per
i naturali
2N-1 ...
2N-1-1] con N-1 bit
fra i positivi
da 0 a 2N-1lo-1zero rientra
Esempio:
su 8 bit, [0,+127]
se MSB=1, stesso intervallo traslato di -2N-1
da -2N-1 a -1
Esempio: su 8 bit, [-128,-1]
Intervallo globale: [-2N-1 , -2N-1-1]
su 8 bit,
[-128,+127]
su 16 bit,
[-32768,+32767]
CONVERSIONE NUMERO / STRINGA
Osservazione: poiché si opera su N bit,
questa è in realtà una aritmetica mod 2N
La rappresentazione del numero v
coincide con quella del numero v + 2N
Conseguenza: possiamo in realtà calcolare la rappresentazione di v’ = v + 2N
v dn1 B
n 2
n1
dk B
v' dn1B
k
k0
n1
È un naturale!
n 2
dk B
k
k0
CONVERSIONE NUMERO / STRINGA
Per calcolare la rappresentazione di v
(v<0) si può calcolare quella di v’ = v + 2N
Esempio (8 bit, 2N = 256):
per calcolare la rappresentazione di -3
calcoliamo quella del naturale 253
-3 = -128 + 125 “11111101”
253 “11111101”
CONVERSIONE NUMERO / STRINGA
Problema: dato un numero negativo v,
come determinare praticamente la sua
rappresentazione in notazione complemento a due?
Poiché sappiamo convertire un naturale in
stringa binaria, il problema diventa:
Partendo dalla rappresentazione binaria
del valore assoluto |v|, come giungere alla
rappresentazione in complemento a due
del valore opposto -|v| ?
CONVERSIONE NUMERO / STRINGA
Partendo dalla rappresentazione binaria
del valore assoluto |v|, come giungere alla
rappresentazione in complemento a due
del valore opposto -|v| ?
• Poiché
v = - |v|
v’ = v + 2N = 2N - |v|
• tutto sta a riuscire a calcolare 2N - |v|...
• ...che però richiede una sottrazione!
CONVERSIONE NUMERO / STRINGA
Fortunatamente, 2N - |v| è una sottrazione
solo in apparenza, in quanto:
2N - |v| = (2N -1)- |v| + 1
Poiché (2N -1) è una sequenza di N “uni”,
calcolare (2N -1)- |v| equivale a invertire
tutti i bit della stringa che rappresenta |v|:
v = -3
|v| “00000011”
(2N -1)- |v|
“11111100”
alla fine basta quindi aggiungere 1.
CONVERSIONE NUMERO / STRINGA
Algoritmo di complementazione a due
Data una stringa di bit che rappresenta il
valore v, per ottenere la stringa che
rappresenta il valore opposto -v occorre:
• invertire tutti i bit della stringa data
• aggiungere 1 al risultato così ottenuto.
Esempi
v = -3 (3 “00000011” )
“11111101”
v = -37 (37 “00100101” ) “11011011”
“11111101” “00000011” |v| = 3 v = -3
CONVERSIONE STRINGA / NUMERO
Problema: data la rappresentazione di un
numero intero in notazione complemento
a due, determinare il valore del numero
Soluzione: applicare la formula
v dn1 B
n 2
n1
dk B
k
k0
oppure: applicare l’algoritmo di complementazione e sfruttare la formula dei naturali per
dedurre il valore assoluto del numero.
OPERAZIONI SU NUMERI INTERI
Rappresentazione in complemento a due
Questa rappresentazione rende possibile
fare addizioni e sottrazioni con le usuali
regole algebriche
Un primo esempio:
-5 +
+3 =
---2
11111011
00000011
-------11111110
Funziona!
OPERAZIONI SU NUMERI INTERI
Rappresentazione in complemento a due
In certi casi occorre però una piccola
convenzione: ignorare il riporto
Un altro esempio:
-1 +
-5 =
---6
11111111
11111011
-------(1)11111010
Funziona…
purché si ignori
il riporto!
OPERAZIONI SU NUMERI INTERI
Rappresentazione in complemento a due
Nelle sottrazioni, analogamente, può
capitare di dover ignorare il prestito
+3 +5 =
--2
(1)00000011 00000101 =
-------11111110
+3 -5 =
-+8
(1)00000011 11111011 =
-------00001000
Ma.. perché ignorando prestiti
e riporti funziona??
OPERAZIONI: PERCHÉ FUNZIONANO
Il motivo è semplice:
poiché si opera su N bit, questa è in realtà
una aritmetica modulare, di modulo 2N
ignorando riporti (o inserendo prestiti)
si introduce un errore pari a 2N
che, quindi, mod 2N scompare!
Nota: possono però prodursi errori se
viene invaso il bit più significativo
ERRORI NELLE OPERAZIONI
In un elaboratore che opera in notazione
complemento a due, si ha errore se si
supera il massimo intero (positivo o negativo) rappresentabile, cioè se si crea un
riporto dal penultimo all’ultimo bit
Esempio:
60 +
75 =
----135
00111100
01100011
-------10011111
Errore!
Si è invaso il bit di
segno, il risultato è
negativo!
(Può capitare solo sommando due positivi o due negativi)
SHIFT CON INTERI IN COMPLEMENTO A 2
La semantica delle operazioni di shift
Shift Left (SHL)
= moltiplicare per 2
Shift Right (SHR) = dividere per 2
è mantenuta in complemento a due?
Sì, purché lo Shift Right (SHR) tenga
conto del segno, ossia
introduca uno 0 da sinistra, se MSB=0
introduca un 1 da sinistra, se MSB=1
Questo shift si chiama Shift Aritmetico
SHIFT CON INTERI IN COMPLEMENTO A 2
Esempio di shift a sinistra:
-10 * 4 = -10 SHL 2
11110110 SHL 2 = 11011000 -40
Esempio di shift (aritmetico) a destra:
-10 / 4 = -10 SHR 2
11110110 SHR 2 = 11111101 -3
Attenzione: lo Shift Right ha la semantica della
divisione intera il quoziente è il massimo
intero minore o uguale a X/Y: non è un semplice
troncamento!
IMPLEMENTARE GLI ALGORITMI
Conversione da stringa a numero
o si applica direttamente la formula
v dn1 B
n 2
n1
oppure
dk B
k
k0
se MSB=0 (positivo) si usa sToNum()
se MSB=1 (negativo), si sfrutta la relazione
v = v’ - 2N, usando sToNum() per ottenere v’,
e sottraendo 2N dal risultato.
funzione sToInt()
IMPLEMENTARE GLI ALGORITMI
Conversione da numero a stringa
se il numero è positivo
si applica l’algoritmo delle divisioni
successive (si ottiene MSB=0)
se invece il numero è negativo
si applica l’algoritmo delle divisioni
successive al numero v’ = v + 2N
(ciò assicura MSB=1)
funzione intToS()
NUMERI REALI
Dominio:
R
Un soprainsieme degli interi
alcune proprietà degli interi potrebbero non essere più verificate
Un numero reale può non essere finitamente rappresentabile come stringa di
simboli
in nessuna base: numeri irrazionali (, e, ...)
in alcune basi: numeri razionali periodici
NUMERI REALI
La rappresentazione di un numero razionale può risultare periodica o meno, a
seconda della base adottata
In particolare, non è mai periodica se si
assume come base il denominatore
della sua forma fratta
1/3 = (0.333333333333...)10 = (0.1)3
8/7 = (1.142857142857...)10 = (1.1)7
...
NUMERI REALI IN DIVERSE BASI
Se la rappresentazione di un numero
razionale in base B è periodica, allora
è periodica anche la rappresentazione
dello stesso numero in base B’ = B/k
il viceversa vale solo se B’ = Bn
Quindi
un numero periodico in base 10 è sicuramente:
periodico anche in base 2 (perché 10 = 2*5)
un numero periodico in base 2 può essere o
non essere periodico in base 10…
… ma lo è certamente in base 4, 8, 16, ...
NUMERI REALI IN DIVERSE BASI
Se la rappresentazione di un numero
razionale in base B è periodica, allora
è periodica anche la rappresentazione
dello stesso numero in base B’ = B/k
il viceversa vale solo se B’ = Bn
se non bastavano i fattori primi
Intuitivamente:
Quindi
della base B a esprimere il numero in forma finita,
un numero periodico in base 10 è sicuramente:
la situazione non può certo migliorare avendo meno
periodico anche in base 2 (perché 10 = 2*5)
fattori a disposizione (B’ = B / k), mentre potrebbe
un numero
periodico
in base 2 può
essere
o
migliorare
avendo
a disposizione
nuovi
fattori
periodico in base 10…
(B” non
= r * essere
B)
… ma lo è certamente in base 4, 8, 16, ...
NUMERI REALI IN DIVERSE BASI
Ovviamente, se B’ = Bn, i fattori primi disponibili
gli stessi (cambia
Serimangono
la rappresentazione
disolo
un l’esponente
numero a cui
compaiono) e quindi la situazione non può
razionale in base B è periodica, allora
cambiare: se era periodico rimane periodico, se era
è non
periodica
rappresentazione
periodicoanche
rimanela
non
periodico.
dello stesso numero in base B’ = B/k
il viceversa vale solo se B’ = Bn
Quindi
un numero periodico in base 10 è sicuramente:
periodico anche in base 2 (perché 10 = 2*5)
un numero periodico in base 2 può essere o
non essere periodico in base 10…
… ma lo è certamente in base 4, 8, 16, ...
NUMERI REALI: MANTISSA E RESTO
Dato un numero reale V, e fissati:
una base B
un naturale N
è sempre possibile esprimere V come
somma di due contributi, di cui il primo
costituito da esattamente N cifre:
V m * Besp + r * Besp-N
mantissa
(n cifre)
esponente
(intero)
resto
(reale)
NUMERI REALI: MANTISSA E RESTO
Esistono infinite triple m, esp, r che
consentono di esprimere, a parità di
B e N, lo stesso numero reale V.
Ad esempio, se V=31.4357, N=4, B=10:
314,3
31,43
3,143
,3143
,0314
,0031
,0003
,0000
Scomposizione
* 10-1 + 570,
* 100 + 57,
* 101 +
5,7
2
* 10 +
,57
3
* 10 +
,357
4
* 10 +
,4357
* 105 +
,14357
* 106 +
,314357
*
*
*
*
*
*
*
*
-1-4
10
100-4
101-4
102-4
103-4
104-4
105-4
106-4
m
m=314.3
m=31.43
m=3.143
m=.3143
m=.0314
m=.0031
m=.0003
m=.0000
r
r=570
r=57
r=5.7
r=.57
r=.357
r=.4357
r=.14357
r=.314357
esp
esp=-1
esp =0
esp =1
esp =2
esp =3
esp =4
esp =5
esp =6
RAPPRESENTAZIONE NORMALIZZATA
Poiché la rappresentazione deve essere
unica, occorre fare una scelta
Si sceglie la tripla m, esp, r tale che
1/B m < 1,
r<1
Rappresentazione normalizzata
Scomposizione
.....
1
3,143 * 10 +
5,7
,3143 * 102 +
,57
,0314 * 103 +
,357
.....
* 101-4
* 102-4
* 103-4
m
....
m=3.143
m=.3143
m=.0314
....
r
....
r=5.7
r=.57
r=.357
....
esp
....
esp =1
esp =2
esp =3
....
RAPPRESENTAZIONE NORMALIZZATA
In pratica, è quelladeve
in cui essere
la man Poiché la rappresentazione
tissa è <1, e la sua prima cifra
unica, occorre fare una scelta
dopo la virgola è diversa da 0
Si sceglie la tripla m, esp, r tale che
1/B m < 1,
r<1
Rappresentazione normalizzata
Scomposizione
.....
1
3,143 * 10 +
5,7
,3143 * 102 +
,57
,0314 * 103 +
,357
.....
* 101-4
* 102-4
* 103-4
m
....
m=3.143
m=.3143
m=.0314
....
r
....
r=5.7
r=.57
r=.357
....
esp
....
esp =1
esp =2
esp =3
....
NUMERI REALI: IL VINCOLO
Un numero reale ha spesso una rappresentazione infinita in una data base, ma
rappresentare infinite cifre è impossibile
Ergo, assumiamo come rappresentazione approssimata del numero reale V il
solo contributo m * Besp
V m * Besp
Il resto si trascura Errore di troncamento
NUMERI REALI: LE SCELTE OPERATIVE
In pratica dobbiamo stabilire:
Quante cifre binarie (bit) per la mantissa?
Quante cifre per l’esponente?
Espresso come?
Come rappresentare il segno del numero?
Osservazione: nel caso B=2, la mantissa
normalizzata è compresa fra 1/2 e 1:
1/2 m < 1
il primo bit dopo la virgola è sempre 1.
NUMERI REALI: LE SCELTE OPERATIVE
In pratica dobbiamo stabilire:
Quante cifre binarie (bit) per la mantissa?
Quante cifre per l’esponente?
Espresso
come?
Ma allora…
si può
evitare di scriverlo
esplicitamente!
In effetti, un
Come rappresentare
il bit
segno del numero?
prefissato non porta informazione!!
Osservazione: nel caso B=2, la mantissa
normalizzata è compresa fra 1/2 e 1:
1/2 m < 1
il primo bit dopo la virgola è sempre 1.
NUMERI REALI: LE SPECIFICHE DEL C
Float (IEEE-32; 4 byte)
1 bit per il segno del numero (0 = +, 1 = -)
8 bit per l’esponente esp, codificato
con eccesso 126 (28-1-2)
valori da 127 a 254 esponenti positivi [1..128]
valori da 1 a 125 esponenti negativi [-125..-1]
i valori estremi (0 e 255) sono riservati
23 bit per la mantissa m (cioè n=24 bit
effettivi, contando l’MSB non rappresentato)
dal byte meno significativo al più significativo
NUMERI REALI: LE SPECIFICHE DEL C
Double (IEEE-64; 8 byte)
1 bit per il segno del numero (0 = +, 1 = -)
11 bit per l’esponente esp, codificato
con eccesso 1022 (211-1-2)
valori da 1023 a 2046 esp. positivi [1..1024]
valori da 1 a 1021 esp. negativi [-1021..-1]
i valori estremi (0 e 2047) sono riservati
52 bit per la mantissa m (cioè n=53 bit
effettivi, contando l’MSB non rappresentato)
dal byte meno significativo al più significativo
NUMERI REALI: LE SPECIFICHE DEL C
Casi particolari:
float
esp=0, m=0
rappresentano 0.0
esp=255, m=0 rappresentano
esp=255, m0 rappresentano un errore
double
esp=0, m=0
rappresentano 0.0
esp=2047, m=0 rappresentano
esp=2047, m0 rappresentano un errore
NUMERI REALI: LE SPECIFICHE DEL C
Valori rappresentabili (lato positivo):
float
[ .1 * 21-126 ... .1111..111*2254-126 ]
[ 2-126 ... 2128 ]
cioè [ 1.2 * 10-38 … 3.4 * 1038 ]
double
[ .1 * 21-1022 ... .1111..111*22046-1022 ]
[ 2-1022 ... 21024 ]
cioè [ 1.3 * 10-308 … 0.7 * 10308 ]
NUMERI REALI: CIFRE SIGNIFICATIVE
Cifre significative
Poiché assumendo V m * Besp trascuriamo il resto r * Besp-N, e poiché nella
forma nornalizzata r<1, l’errore vale:
Eassoluto Besp-N
Esso non è molto significativo in sé: lo è
di più se rapportato al valore del numero
Erelativo Besp-N / (m * Besp)
da cui, poiché 1/B m < 1,
Erelativo Besp-N / Besp-1 = B1-N
NUMERI REALI: CIFRE SIGNIFICATIVE
Cifre significative
Se dunque Erelativo B1-N , le cifre decimali
significative risultano:
float
N=24 Er 2-23 = 10-23*log2 = 10-7
circa 7 cifre decimali significative
double
N=53 Er 2-52 = 10-52*log2 = 10-15
circa 15 cifre decimali significative
NUMERI REALI: CIFRE SIGNIFICATIVE
Cifre significative Epsilon di macchina: il più
float
chedecimali
la macchina
Se dunque Erelativo piccolo
B1-N , le
cifre
distingue come diverso da 0
significative risultano:
float
N=24 Er 2-23 = 10-23*log2 = 10-7
circa 7 cifre decimali significative
double
N=53 Er 2-52 = 10-52*log2 = 10-15
circa 15 cifre decimali significative
il più piccolo
double distinguibile da 0
NUMERI REALI: ESEMPIO 1
Rappresentazione come float di V = 1.0
rappr. normalizzata: V = 1.010 = 0.12 * 21
segno (1 bit):
0
mantissa (24 bit):
.10000000 00000000 00000000
esponente (8 bit con eccesso 126)
esp=1 126+1 = 127 01111111
segno
0
esponente
0111 1111
mantissa normalizzata (23 bit, MSB escluso)
000 0000 0000 0000 0000 0000
in memoria:
byte 1
0011 1111
byte 2
1000 0000
byte 3
0000 0000
byte 4
0000 0000
NUMERI REALI: ESEMPIO 2
Rappresentazione come float di V = 5.875
rappr. normalizzata: V = 101.1112 = .1011112 * 23
segno (1 bit):
0
mantissa (24 bit):
.10111100 00000000 00000000
esponente (8 bit con eccesso 126)
esp=3 126+3 = 129 10000001
segno
0
esponente
1000 0001
mantissa normalizzata (23 bit, MSB escluso)
011 1100 0000 0000 0000 0000
in memoria:
byte 1
01000000
byte 2
1011 1100
byte 3
0000 0000
byte 4
0000 0000
NUMERI REALI: ESEMPIO 3
Rappresentazione come float di V = -29.1875
rappr. normalizzata: V = - .1110100112 *25
segno (1 bit):
1
mantissa (24 bit):
.11101001 10000000 00000000
esponente (8 bit con eccesso 126)
esp=5 126+5 = 131 10000011
segno
1
esponente
1000 0011
mantissa normalizzata (23 bit, MSB escluso)
110 1001 1000 0000 0000 0000
in memoria:
byte 1
1100 0001
byte 2
1110 1001
byte 3
1000 0000
byte 4
0000 0000
NUMERI REALI: ESEMPIO 4
Rappresentazione come float di V = 0.110
rappr. normalizzata: V =.0(0011)2
periodico!
segno (1 bit):
0
mantissa (24 bit):
.11001100 11001100 11001100
esponente (8 bit con eccesso 126)
esp=-3 126-3 = 123 01111011
segno
0
esponente
0111 1011
mantissa normalizzata (23 bit, MSB escluso)
100 1100 1100 1100 1100
in memoria:
byte 1
0011 1101
byte 2
1100 1100
byte 3
1100 1100
byte 4
1100 1100
NUMERI REALI: ESEMPIO 4
Rappresentazione comeErrore
float di
V = 0.110
di troncamento
o si tronca periodico!
o si arrotonda
rappr. normalizzata: V =.0(0011)
2
il C arrotonda
segno (1 bit):
0
mantissa (24 bit):
.11001100 11001100 11001101
esponente (8 bit con eccesso 126)
esp=-3 126-3 = 123 01111011
segno
0
esponente
0111 1011
mantissa normalizzata (23 bit, MSB escluso)
100 1100 1100 1100 1101
in memoria:
byte 1
0011 1101
byte 2
1100 1100
byte 3
1100 1100
byte 4
1100 1101
NUMERI REALI: ESEMPIO 5
Rappresentazione come float di V = 0.1510
rappr. normalizzata: V =.00(1001)2 periodico!
segno (1 bit):
0
mantissa (24 bit):
.10011001 10011001 10011010
esponente (8 bit con eccesso 126)
esp=-2 126-2 = 124 01111100
segno
0
esponente
0111 1100
mantissa normalizzata (23 bit, MSB escluso)
001 1001 1001 1001 1001 1010
in memoria:
byte 1
0011 1110
byte 2
0001 1001
byte 3
1001 1001
byte 4
1001 1010
NUMERI REALI: ESEMPIO 6
Rappresentazione come float di V = -1/310
rappr. normalizzata: V = -.(01)2
periodico!
segno (1 bit):
1
mantissa (24 bit):
.10101010 10101010 10101011
esponente (8 bit con eccesso 126)
esp=-1 126-1 = 125 01111101
segno
1
esponente
0111 1101
mantissa normalizzata (23 bit, MSB escluso)
010 1010 1010 1010 1010 1011
in memoria:
byte 1
1011 1110
byte 2
1010 1010
byte 3
1010 1010
byte 4
1010 1011
CONVERSIONE STRINGA / NUMERO
Problema: data la rappresentazione di un
numero reale in una certa base,
determinare il valore del numero
Soluzione: applicare la formula
v
n1
d B
k m
k
k
Bisogna considerare anche potenze
negative di B, con le cifre dopo la virgola.
CONVERSIONE STRINGA / NUMERO
Esempio: calcolare il valore rappresentato
dalla stringa 1001.112
v
n1
d B
k m
k
k
Soluzione: si sommano i singoli contributi
V = 23 + 20 + 2-1 + 2-2 = 9.7510
Operativamente, con i naturali si valutava
il polinomio con il metodo di Horner.
E adesso?
CONVERSIONE STRINGA / NUMERO
Conviene separare il calcolo della parte
intera da quello della parte frazionaria:
v
1
n1
d B d B
k
k m
k
k 0
k
k
Per il calcolo del valore della parte intera si
può usare ancora l’algoritmo di Horner.
Per il calcolo del valore della parte frazionaria
si può adottare un algoritmo analogo.
CONVERSIONE STRINGA / NUMERO
Calcolo del valore della parte frazionaria:
1
d B
k m
k
k
((..((d m / B) dm 1 )/ B) ...d1 ) / B)
L’algoritmo di Horner raccoglieva via via il fattore
B, questo raccoglie il fattore 1/B.
Esempio: .11012 (valuta da destra a sinistra)
V = (((((1 / 2 + 0) / 2) + 1) / 2 + 1) / 2) = 0.812510
CONVERSIONE NUMERO / STRINGA
Per convertire un numero in una stringa
di cifre, l’essenziale è riuscire a “isolare”
e ricavare le singole cifre.
Nel caso dei naturali, lo si fa con
l’algoritmo delle divisioni successive:
dk = vk % B
il quoziente vk+1 = vk / B consente di iterare
Per la parte frazionaria occorre dunque
un algoritmo analogo.
CONVERSIONE NUMERO / STRINGA
Algoritmo delle moltiplicazioni successive
si moltiplica v per B
la parte intera che si genera costituisce la
cifra più significativa
la parte frazionaria itera il procedimento
se prima o poi la parte frazionaria si
azzera, il numero è rappresentabile in
forma finita in tale base;
se invece si rigenera la stessa serie di
cifre, siamo di fronte a un numero
periodico in tale base.
UN ESEMPIO
Calcolare la rappresentazione binaria del
numero V=0.87510
.875 * 2 = 1.75
parte intera = 1, parte frazionaria restante = .75
.75 * 2 = 1.5
parte intera = 1, parte frazionaria restante = .5
.5 * 2 = 1.0
parte intera = 1, parte frazionaria restante = .0
Rappresentazione risultante: .1112
(non periodico)
UN ALTRO ESEMPIO
Calcolare la rappresentazione binaria del
numero V=0.1510
.15 * 2 = 0.3
parte intera = 0
.3 * 2 = 0.6
parte intera = 0
.6 * 2 = 1.2
parte intera = 1
.2 * 2 = 0.4
parte intera = 0
.4 * 2 = 0.8
parte intera = 0
.8 * 2 = 1.6
parte intera = 1
.6 * 2 = 1.2
si ripete la sequenza!!
Rappresentazione (periodica): .00(1001)2
DENTRO LA MACCHINA C
E se volessimo “spiare” dentro la macchina virtuale C per vedere la rappresentazione fisica dei numeri?
p
Occorre
x
ricavare l’indirizzo
della variabile
esplorare quell’area di memoria
byte per byte, per tanti byte
quanta la dimensione di quel tipo
di dato (es: float = 4 byte)
visualizzare ogni byte.
&x
9A
99
19
3E
Esempio: x = 0,15
DENTRO LA MACCHINA C
Un programma:
main() {
cast: ci serve un puntatore a
byte, che in C si esprime
come “unsigned char”
float x; int i;
unsigned char* p = (unsigned char*) &x;
printf("Float: "); scanf("%f",&x);
printf("\nRappr. interna di %f:\n", x);
for (i=0; i<sizeof(x); i++)
printf("Byte %i:\t%X\n",i, p[i] );
printf("\n");
i-esimo byte
}
sizeof: dà la dimensione in
byte di quella variabile
esadecimale
OPERAZIONI FRA REALI & ERRORI
Negli interi, si possono creare errori nelle
operazioni, ma gli operandi sono
comunque rappresentati esattamente
Nei reali, invece, già gli stessi operandi
possono essere affetti da errore, a causa
dell’impossibilità di rappresentare le
infinite cifre dei numeri periodici e
irrazionali: è l’errore di troncamento.
Errore di troncamento = ogni qual volta il
numero di cifre disponibili è insufficiente.
ERRORE DI TRONCAMENTO
Si manifesta quando
il numero è periodico
il numero non è periodico ma ha troppe cifre
il risultato di un’operazione, a causa un riporto, richiede troppe cifre
Esempi (mantissa di 8 bit, per semplicità)
15.810 = .1111110011001100... * 24
301.510 = .10010110012 * 29
15110 + 16010 =
= .10010111 * 28 + .10100000 * 28 =
= .100110111 * 29
(è 15.75)
(è 300)
(è 310)
OPERAZIONI FRA REALI & ERRORI
Vi sono poi altre due sorgenti di errore:
l’errore di incolonnamento
è causato dalla necessità di incolonnare i
numeri per poterli sommare o sottrarre
l’errore di cancellazione
è la conseguenza finale della presenza, a
monte, di errori di troncamento, che possono
far sì che alcune cifre del risultato non siano
affidabili, ovvero siano “virtualmente
cancellate”
si manifesta sottraendo numeri simili fra loro.
ERRORE DI INCOLONNAMENTO
L’errore di incolonnamento è causato
dalla necessità di incolonnare i numeri per
poterli sommare o sottrarre
per incolonnare due numeri che, in forma
normalizzata, hanno esponente diverso
occorre necessariamente “de-normalizzarne”
uno
si allinea quello di valore assoluto minore a
quello di valore assoluto maggiore
ciò causa una perdita di cifre significative nel
numero che viene “de-normalizzato”.
ERRORE DI INCOLONNAMENTO
Esempio: 96.5 + 1.75
Ipotesi: mantissa di 8 bit (per semplicità)
96.510 =.110000012 * 27 (senza errore)
1.7510 =.111000002 * 21 (senza errore)
Somma:
.110000012 * 27 +
.110000012 * 27 +
.111000002 * 21 =
.000000112 * 27 =
cifre perse:
è l’errore di
incolonnamento
.110001002 * 27
È 98, non 98.25 come doveva!
ERRORE DI CANCELLAZIONE
L’errore di cancellazione è può presentarsi quando si sottraggono numeri assai
simili fra loro
accade solo se in almeno uno dei due operandi, all’inizio, vi è stato errore di troncamento
consiste nel fatto che si introducono zeri da
destra per normalizzare il risultato, ma quegli
zeri non sono significativi
se ci fossero state le cifre perse all’inizio a
causa del troncamento, il risultato sarebbe
stato diverso.
ERRORE DI CANCELLAZIONE
Esempio: 15.8 + 15.5
Ipotesi: mantissa di 8 bit (per semplicità)
15.810 =.111111002 * 24 (con errore tronc.)
15.510 =.111110002 * 24 (senza errore)
Differenza:
cifre cancellate:
è l’errore di
.111111002 * 24 cancellazione
4
.111110002 * 2 =
.000001002 * 24 = . 100000002 * 2-1
Sono 0 solo perché abbiamo troncato 15.8
all’inizio: avrebbero dovuto essere 11001
ERRORI: CONSEGUENZE
A causa di questi errori, la proprietà
associativa può non essere più verificata
Esempio (mantissa di 8 bit, per semplicità)
X = 0,75 .11000000 * 20 (senza errori)
Y = 65,6 .10000011 * 27 (err. troncamento)
Z = 64,0 .10000000 * 27 (senza errori)
si ha che
(X + Y) - Z
è diverso da
X + (Y - Z)
ERRORI: CONSEGUENZE
(X + Y) - Z
è diverso da
Primo caso:
(X + Y) - Z
Prima operazione: A = X + Y
.11000000 * 20 +
.10000011 * 27 =
.00000001 * 27 +
.10000011 * 27 =
.10000100 * 27 A
(err. incolonnamento)
Seconda operazione: R = A - Z
.10000100 * 27 .10000000 * 27 =
.00000100 * 27 =
.100????? * 22 =
.10000000 * 22 R
(da rinormalizzare)
(errore cancellazione)
R = .10000000 * 22
X + (Y - Z)
Secondo caso: X + (Y - Z)
Prima operazione: A = Y - Z
.10000011 * 27 .10000000 * 27 =
.00000011 * 27 =
.11?????? * 21 =
.11000000 * 21 A
(da rinormalizzare)
(errore cancellazione)
Seconda operazione: R = X + A
.11000000 * 20 +
.11000000 * 21 =
.01100000 * 21 +
.11000000 * 21 =
1.00100000 * 21
.10010000 * 22 R
(err. incolonnamento)
(da rinormalizzare)
(err. tronc. potenziale)
R = .10010000 * 22
ACCUMULAZIONE DI ERRORI
La presenza di errori che si accumulano
può portare a risultati totalmente assurdi
Esempio:
Calcolo di con l’algoritmo di Euclide
Una circonferenza di raggio 1 è lunga 2,
che può essere approssimata:
dall’esterno, dal perimetro del poligono
regolare di n lati circoscritto
dall’interno, dal perimetro del poligono
regolare di n lati inscritto
ACCUMULAZIONE DI ERRORI
Valgono le relazioni:
ln = lato del poligono di n lati inscritto
Ln = lato del poligono di n lati circoscritto
= 2 l / (4 - l2)
Ln
ln
Ln = lunghezza lato poligono
circoscritto di n lati
ln = lunghezza lato poligono
inscritto di n lati
ACCUMULAZIONE DI ERRORI
Una funzione che implementa l’algoritmo:
void pigrecoFloat(void) {
float eps, LN, smpinf, smpsup, nlati, OC2, diff;
printf("Calcolo di pigreco con FLOAT. "
"Precisione [1e-8] ? ");
scanf("%f", &eps);
nlati = 4.0; LN = sqrt(2.0);
do {
OC2 = sqrt(4.0 - LN * LN); nlati *= 2.0;
diff = 2.0 - OC2; LN = sqrt(diff);
smpinf = LN * nlati / 2.0;
smpsup = LN * nlati / OC2;
printf("nl=%10.0f d2=%f piInf=%f piSup=%f\n",
nlati, OC2, smpinf, smpsup);
} while ((smpsup-smpinf >= eps) && (nlati < 1e+19));
}
ACCUMULAZIONE DI ERRORI
… e il suo output:
Calcolo di pigreco con
nl=
8 d2=1.414214
nl=
16 d2=1.847759
nl=
32 d2=1.961571
nl=
64 d2=1.990369
nl= 128 d2=1.997591
nl= 256 d2=1.999398
nl= 512 d2=1.999849
nl= 1024 d2=1.999962
nl= 2048 d2=1.999991
nl= 4096 d2=1.999998
nl= 8192 d2=1.999999
nl=16384 d2=2.000000
nl=32768 d2=2.000000
FLOAT. Precisione ? 1E-8
piInf=3.061467 piSup = 4.329569
piInf=3.121444 piSup = 3.378627
piInf=3.136546 piSup = 3.197995
piInf=3.140333 piSup = 3.155528
piInf=3.141286 piSup = 3.145074
piInf=3.141519 piSup = 3.142465
piInf=3.141208 piSup = 3.141444
piInf=3.142451 piSup = 3.142510
piInf=3.142451 piSup = 3.142466
piInf=3.162278 piSup = 3.162282
piInf=3.162278 piSup = 3.162279
piInf=2.828427 piSup = 2.828427
piInf=0.000000 piSup = 0.000000
CONVERSIONE DA INTERI A REALI
Nelle espressioni che coinvolgono interi e
reali, i numeri interi devono essere convertiti in rappresentazione reale per poter
eseguire le operazioni
Non si può semplicemente “spostare la
virgola”, perché la rappresentazione in
complemento a due non è posizionale
Esempio:
N = -8 (intero, 1 byte)
R = -8.0 = .1*23
segno
11111000
1 10000001 0000000
esp (126+3)
mantissa
ESERCIZIO
Dire come vengono svolte le seguenti
espressioni, calcolandole passo passo
Ipotesi:
interi rappresentati in complemento a due su
un byte (8 bit da -128 a +127)
reali rappresentati su due byte (1 bit di segno,
8 di esponente con eccesso 126, 7 di mantissa)
Esercizio
int i=10; float a=0.6, b, c;
b = a + i - 8; c = a + (i - 8);
FUNZIONI DI CONVERSIONE STANDARD
La libreria standard stdlib fornisce quasi
tutte le funzioni di conversione già pronte
da stringa a numero
atoi()
atol()
atof()
da stringa a intero
da stringa a long
da stringa a double
da numero a stringa (solo Turbo C)
itoa()
ltoa()
fcvt()
da intero a stringa
da long a stringa
da double a stringa
Il C standard
usa sprintf(),
che vedremo
più avanti.