Esercitazione 4: Lunedi 24 Novembre 2008
Esercizio 1
Floating point
In un elaboratore i numeri reali si possono rappresentare utilizzando la
rappresentazione in virgola mobile (floating point)
N * exp(B,M) normalizzata (cioe’ una
sola cifra prima della virgola es. 123.45 à 1.2345*10^2, 0.1234 à
1.234*10^{-1}.
La precisione della rappresentazione dipende quindi dal numero di bit utilizzati
per rappresentare N, B ed M.
In C/C++ si utilizzano i tipi float (4byte) e double (8byte) con
rappresentazione in binario delle varie componenti (esponente,
mantissa, caratteristica).
Questo tipo di rappresentazione puo’ generare risultati inaspettati dovuti ai
limiti di precisione della macchina.
Come primo esempio provate ad eseguire il seguente codice:
#include
<iostream.h>
main()
{
double prec=1.0;
while ((1.+ prec)!=1.0) prec/=2.;
prec*=2;
cout<<prec;
}
Questo programma stampa un valore che rappresenta la precisione nella
rappresentazione di un numero double (calcoliamo la serie 1/(2^n) , fino a che
il valore diventa 0 a causa
dei limiti nella rappresentazione dei razionali con un double).
Notate quindi che lavorando con i double puo’ essere rischioso usare dei
controlli del tipo:
if
(result == expectedResult) ….
Infatti questo test potrebbe fallire a causa di possibili arrotondamenti nel
calcolo di result.
Conviene invece utilizzare un confronto del tipo:
if (fabs(result - expectedResult) <
epsilon) ….
dove fabs=valore assoluto ed epsilon indica la precisione
scelta (se il la differenza con il risultato atteso e’ nell’intervallo
[0,…,ipsilon) allora)
In questo esempio si utilizza un errore assoluto (indipendente dalla grandezza
dei valori confrontati).
Questo tipo di confronto non e’ sempre adeguato.
Ad esempio provate ad includere in un programma
#include <iostream.h>
main()
{
float expectedResult =
10000;
float result = +10000.000977; // The closest 4-byte float to 10,000 without
being 10,000
float diff = fabs(result - expectedResult);
cout << diff;
}
La precisione (diff) in questo caso non aiuta a valutare la
“vicinanza” dei due numeri.
Si ovviare a questo tipo di problemi si puo’ utilizzare as esempio un confronto
con errore relativo (percentuale) del tipo:
if ((fabs(result –
expectedResult)/expectedResult) < epsilon) ….
chiaramente ponendo attenzione ad evitare di dividere per 0…
Per leggete interessante articolo su questo argomento: Confronto
tra floating point (inglese).
Casting
Quando si usano le operazioni su
interi e reali bisogna stare molto attenti al tipo degli argomenti.
Infatti il C esegue delle conversioni implicite (ad es. da float a int).
Ad esempio provate ed eseguire il seguente frammento di codice e a stampare i valori risultanti:
int numerointero;
int
numerointero2=10;
float numerofloat=6.34;
float numerofloat2;
numerointero=(int)numerofloat; /* assegna il valore 6 (parte intera) */
numerofloat2=(float)numerointero2 /* assegna 10.0 (valore float) */
In operazioni quali la divisione il tipo degli argomenti puo’ influire sul risultato.
Considerate ad esempio l’assegnamento:
z=3/2
dove z avra' valore 1, anche se e' stato dichiarato come float
Infatti se entrambi gli argomenti della divisione sono integer, allora verra' effettuata una divisione integer;
per avere un risultato corretto sara' necessario scrivere:
z=3.0/2 oppure z=3/2.0 o z=3.0/2.0
Per forzare la conversione di tipo in C si puo’ usare il ``casting” di tipo.
Ad esempio, se abbiamo due numeri integer come operatori e
vogliamo che il risultato sia un float, allora dovremo agire come segue:
int intnumber,anotherint;
float floatnumber;
floatnumber=(float)intnumber/(float)anotherint
Questa operazione assicura una divisione in floating-point.
Libreria cmath
Le funzioni matematiche piu comuni (abs, fabs, sin, cos, tan, exp, …su float e double) sono definite nella libreria cmath.
Per usarle vanno prima importate
con la direttiva #include<cmath> come nell’esempio seguente:
#include
<iostream>
#include <cmath>
using namespace std;
float f(float
x); //definite dopo il main
int
main() {
float x;
cout << "Inserisci un numero
(anche decimale tipo 0.3): x = ";
cin >> x;
cout << f(x); //stampo f(x)
return 0;
}
float f(float x){
return log(x)+cos(x);
}
log(x) e’ il logaritmo naturale di x, cos(x) e’ il coseno di x.
Provate ad eseguire il programma con diversi input (es. con 0…).
Scrivere un programma C che implementa il metodo
di bisezione per calcolare un’approssimazione di una soluzione
dell’equazione f(x)=0 in un intervallo [a,b]
per una funzione f (definita a priori) che soddisfa le ipotesi del teorema
degli zeri in [a,b] (f e’ continua e f(a)*f(b)<0).
Usare ad esempio la funzione f(x)=log(x)+cos(x) nell’intervallo [0.3,0.5] (vedi programma esercizio precedente).
Scrivere ora un programma C che
implementa il metodo
delle corde per calcolare un’approssimazione di una soluzione dell’equazione
f(x)=0.