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…).

 

 

 

Esercizio 2

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).

 

Esercizio 3

Scrivere ora un programma C che implementa il metodo delle corde per calcolare un’approssimazione di una soluzione dell’equazione f(x)=0.