Numeri reali in Java: cenni

Transcript

Numeri reali in Java: cenni
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 (p, 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, è periodica anche la rappresentazione in
base B’ = B/k; il viceversa vale solo se B’ = Bn
· 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, ...
· Intuitivamente: se non bastavano i fattori primi della base
B a esprimere il numero in forma finita, la situazione non
può certo migliorare avendo meno fattori a disposizione
(come accade con B’ = B / k), mentre potrebbe migliorare
avendo a disposizione nuovi fattori (come con B” = r * B)
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:
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
RAPPRESENTAZIONE NORMALIZZATA
In pratica, è quella
in cui
la man· Poiché la rappresentazione
deve
essere
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
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:
·
·
·
·
il numero N di cifre binarie (bit) per la mantissa
il numero P di cifre binarie (bit) per l’esponente
la codifica da adottare per l'esponente
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:
· il numero N di cifre binarie (bit) per la mantissa
· ilallora
numero
P dievitare
cifre binarie
(bit) per
l’esponente
Ma
si può
di scriverlo
esplicitamente!
· In
un da
bit adottare
prefissatoper
nonl'esponente
porta informazione.
la effetti,
codifica
Dunque,
N cifre binarie ilrichiedono
la memoriz· come rappresentare
segno delper
numero
zazione soltanto N-1 bit.
· 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 (IEEE-754)
NUMERI REALI: LE SPECIFICHE
Casi speciali (espMAX=255 per i float, 2047 per i double):
· esp=0,
m=0 rappresenta 0.0
· esp=espMAX
m=0 rappresenta ± ¥
· esp=espMAX
m¹0
rappresenta NaN
dove NaN (Not a Number) indica il risultato di operazioni
indeterminate o impossibili:
0/0
0*¥
¥-¥
¥/¥
· esp=0
m¹0 rappresenta valori non
normalizzati
ciò consente di rappresentare numeri ancora più piccoli,
ampliando il range di valori rappresentabili a parità di costo.
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
Er £ 2-23 = 10-23*log2 = 10-7
· circa 6-7 cifre decimali significative
· N=24
· double
Er £ 2-52 = 10-52*log2 = 10-15
· circa 14-15 cifre decimali significative
· N=53
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
in memoria:
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
in memoria:
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
in memoria:
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
in memoria:
NUMERI REALI: ESEMPIO 4
Rappresentazione come float
di di
V troncamento
= 0.110
Errore
·
·
·
·
o si tronca o si arrotonda
rappr. normalizzata: V =.0(0011)2 periodico!
di solito si arrotonda
segno (1 bit):
0
mantissa (24 bit):
.11001100 11001100 11001101
esponente (8 bit con eccesso 126)
esp=-3 ® 126-3 = 123 ® 01111011
in memoria:
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
in memoria:
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
in memoria:
ESPERIMENTI: SPIARE DENTRO JAVA
Come verificare se quanto calcolato è esatto?
· Occorre un modo per "spiare" dentro la macchina
virtuale del linguaggio (Java, C, Pascal, …)
· In Java,
la classe wrapper Float (risp. Double) fornisce la
funzione floatToIntBits (risp. doubleToLongBits)
· Tale funzione "estrae" la rappresentazione di un float
(risp. double) bit per bit e la pone in un int (risp. long)
pronta per la stampa o altre elaborazioni.
ESEMPIO: SPIARE UN FLOAT in Java
Esempio di codice che "spia" un float f:
System.out.println("Numero float: " + f);
int internalRep = Float.floatToIntBits(f);
// converto l'intero in stringa binaria
// tramite una variante di toString
String binaryString =
Integer.toBinaryString(internalRep);
// aggiungo '0' davanti fino ad avere 32 bit
// dato che toBinaryString non mette zeri iniziali
while (binaryString.length()<32)
binaryString = "0" + binaryString;
// stampo la stringa a video
System.out.println(binaryString);
ESPERIMENTI: MIGLIORIE ESTETICHE
• La versione precedente stampa un'unica stringa di 32
bit, praticamente illeggibile per noi:
10111110101010101010101010101011
• meglio sarebbe separare le varie parti, ad esempio:
1 01111101 01010101010101010101011
o magari:
1 01111101 0101010 10101010 10101011
Per farlo:
System.out.println(
binaryString.charAt(0) + " " +
binaryString.substring(1,9) + " " +
binaryString.substring(9,16) + " " +
binaryString.substring(16,24) + " " +
binaryString.substring(24,32) );
OPERAZIONI FRA REALI & ERRORI
· Negli interi, si possono creare errori nelle
operazioni, ma gli operandi sono comunque
rappresentati esattamente (entro il range)
· Nei reali, invece, già gli stessi operandi possono essere già 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
· 300.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
bisogna per forza“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 =
.110001002 * 27
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 +
.11100000
.000000112 * 27 =
cifre perse: 2 * 21 =
è l’errore di
incolonnamento
È 98, non 98.25 come doveva! 7
.110001002 * 2
ERRORE DI INCOLONNAMENTO:
ESPERIMENTO
public static void test() {
double val1 = 12345.0;
double val2 = 1e-16; // proprio al limite…
// errore di incolonnamento:
// val1 + val2 COINCIDE con val1 !
if (val1 + val2 == val1)
System.out.println("val1 + val2 == val1 !!");
}
---Java Run--val1 + val2 == val1 !!
ERRORI: ESPERIMENTO
public static void test1() {
double val1 = 0.1;
double val2 = 0.3;
// sembra 0.1 .. ma non lo è !
// sembra 0.3 .. ma non lo è !
System.out.println(val1); // stampa 0.1 .. ma è un'illusione !
System.out.println(val2); // stampa 0.3 .. ma è un'illusione !
// … e infatti, il triplo di val1 non è val2 !!!
double d = val1 + val1 + val1; // non è il triplo di val1!
if (d != val2) { System.out.print("ERRORE di ");
System.out.println(d - val2);}
// differenza di 5.5E-17
}
val1 = 0.1
val2 = 0.3
ERRORE di 5.551115123125783E-17
ERRORE DI CANCELLAZIONE
L’errore di cancellazione può presentarsi
quando si sottraggono due numeri molto
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:
.111111002 * 24 .111110002 * 24 =
.000001002 * 24 = . 100000002 * 2-1
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)
cifre cancellate:
Differenza:
è l’errore di
cancellazione
4
.111111002 * 2 .111110002 * 24 =
Sono 0 solo perché abbiamo troncato 15.8
-1 11001
.000001002 all’inizio:
* 24 = avrebbero
. 10000000
*
2
dovuto
essere
2
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 ®
troncamento)
· Z = 64,0 ®
.10000011 * 27 (err.
.10000000 * 27 (senza errori)
· ORRORE!
(X + Y) - Z
è diverso da
X + (Y - Z)
ERRORI: CONSEGUENZE
(X + Y) - Z
è diverso da
R = .10000000 * 22
X + (Y - Z)
R = .10010000 * 22
ACCUMULAZIONE DI ERRORI
· La presenza di errori che si accumulano
può portare a risultati totalmente assurdi
· Esempio:
Calcolo di p con l’algoritmo di Euclide
Una circonferenza di raggio 1 è lunga 2p,
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 – l 2)
L'ALGORITMO IN JAVA (1/2)
import java.io.*;
public class PigrecoEuclide {
public static void main(String[] args) {
System.out.println("Calcolo di pigreco con FLOAT.");
System.out.print("Precisione [1e-8] ? ");
float eps = 1E-8F; // inizializz. richiesta da eccezione
try{
BufferedReader kbd = new BufferedReader(
new InputStreamReader(System.in));
eps = Float.parseFloat(kbd.readLine());
}
catch(IOException e){
System.err.println("Errore di input - Program exit");
System.exit(1);
}
...
L'ALGORITMO IN JAVA (2/2)
...
// algoritmo
float nlati = 4.0F, ln = (float) Math.sqrt(2.0);
float smpinf, smpsup;
do {
float OC2 = (float) Math.sqrt(4.0 - ln * ln);
nlati *= 2.0;
ln = (float) Math.sqrt(2.0F - OC2);
smpinf = ln * nlati / 2.0F;
smpsup = ln * nlati / OC2;
System.out.println("nl=" + nlati + " d2=" + OC2 +
" piInf=" + smpinf + " piSup=" + smpsup);
} while ((smpsup-smpinf >= eps) && (nlati < 1e+19));
}
}
L'OUTPUT
Calcolo di pigreco con FLOAT.
Precisione (suggerito 1e-8)? 1e-8
nl=8.0
nl=16.0
nl=32.0
nl=64.0
nl=128.0
nl=256.0
nl=512.0
nl=1024.0
nl=2048.0
nl=4096.0
nl=8192.0
nl=16384.0
nl=32768.0
d2=1.4142137 piInf=3.0614672
d2=1.8477591 piInf=3.1214445
d2=1.9615706 piInf=3.1365461
d2=1.9903694 piInf=3.1403334
d2=1.9975909 piInf=3.1412857
d2=1.9993976 piInf=3.1415188
d2=1.9998494 piInf=3.1412080
d2=1.9999623 piInf=3.1424513
d2=1.9999906 piInf=3.1424513
d2=1.9999976 piInf=3.1622777
d2=1.9999994 piInf=3.1622777
d2=1.9999999 piInf=2.8284270
d2=2.0 piInf=0.0 piSup=0.0
piSup=4.329568
piSup=3.378627
piSup=3.1979947
piSup=3.155528
piSup=3.1450741
piSup=3.1424654
piSup=3.1414444
piSup=3.1425104
piSup=3.142466
piSup=3.1622815
piSup=3.1622787
piSup=2.8284273