Esercizio 1 (Codifica binaria in virgola mobile) Codificare in virgola mobile a precisione singola I seguenti numeri: N1 = 16; N2 = -1.1 Soluzione: Un numero in virogla mobile è sempre composto in questo modo: (-1)S * 1.M * 2(E + 127) Il numero N1:16 è rappresentabile come (-1)0 * 1.0 * 2(4 + 127) Di conseguenza la codifica di N1 : S = 0 E = 10000011 M = 00000000000000000000000 Il numero N2:-1.1 è rappresentabile come (-1)1 * 1.1 * 2(-127 + 127) . Questo è uno dei numeri che non ammettono rappresentazione esatta in virgola mobile. Avremo dunque una mantissa periodica. N2 : S = 1 E = 01111111 M = 00011001100110011001101 Esercizio 2 (Codifica binaria in virgola mobile) Codificare in virgola mobile a precisione singola Ii seguente numero N: 10.6875 Soluzione: Per codificare un numero qualsiasi in virgola mobile esiste un semplice algoritmo: 1) si converte la parte intera in binario; 2) si converte la parte decimale in binario; 3) si mettono insieme le rappresentaizioni in binario, separate dalla virgola, ottenendo la codifica in virgola fissa; 4) si sposta la virgola verso sinistra fino a che non rimanga un solo 1 a sinistra della virgola. La mantissa è rappresentata dai bit a destra della virgola. In caso di necessità si aggiungono tanti 0 a destra fino ad arrivare al numero di bit richiesti per la mantissa 5) Si tiene conto degli spostamenti della virgola effettuati verso sinistra e per ognuno di essi si aumenta di 1 la potenza di 2 per cui verrà moltiplicata la mantissa. L'esponente di 2 va aumentato di 127. In questo modo si ottiene l'esponente del numero in virgola mobile. 6) Si determina il bit di segno: 0 se positivo, 1 se negativo. Nel caso di 10.6875 1010 = 10102 La conversione viene effettuata con il solito algoritmo per la conversione di un numero decimale intero in binario (Vedi esercitazioni 1 e 2) 0.687510 = 10112 La conversione si effettua con il solito algoritmo per la conversione di un numero decimale frazionario in binario (Vedi esercitazioni 1 e 2). Si moltiplica la sola parte frazionaria del numero per 2 e si considera la parte intera del risultato come bit per la codifica. L'algoritmo termina quando si ottiene un numero intero come risultato di una moltiplicazione o quando termina il numero di bit disponibili per rappresentare il numero. Nel nostro caso: 0.6875 *2 (1)0.375 *2 Nel caso che si ottenga un numero >1 per l'iterazione successiva si considera di nuovo solo la parte frazionaria. L'1 viene comunque considerato per la codifica. (0).75 * 2 (1).5 *2 (1).000 Alla fine si leggono i bit di codifica ottenuti nell'ordine in cui si sono ottenuti. Per ricavare la mantissa scriviamo i due numeri ottenuti uno di fianco all'altro separati dal . : 1010.1011 Spostiamo il . Verso sinistra fino a quando non rimane solo un 1 a sinistra del . Contiamo anche gli sposptamenti fatti e usiamo il numero di spostamenti come esponente del 2 per cui viene moltiplicata la mantissa: 1.0101011 *23 La mantissa M = 01010110000000000000000 L'esponente E si ottiene sommando 127 al 3 esponente in 23. Si codifica 127+3 = 130 in binario E = 10000010 Il segno, visto che si tratta di un numero positivo è: S=0 La codifica di N è la seguente: S = 0 E = 10000010 M = 01010110000000000000000 Esercizio 3 (Fibonacci) a) Scrivere un programma che chieda un intero positivo n all’utente e in risposta stampi i primi n numeri della serie di Fibonacci. La serie di fibonacci ` definita così: F (n) = F (n − 1) + F (n − 2) F (0) = 1 F (1) = 1 Quindi la serie è: F (0) = 1 F (1) = 1 F (2) = 1+1=2 F (3) = 1+2=3 F (4) = 2+3=5 F (5) = 3+5=8 ... b) Il programma stampa il numero appartenente alla serie di fibonacci più vicino (maggiore o minore) al numero digitato dall’utente. Se il numero digitato dall’utente è equidistante da due numeri della serie di Fibonacci, entrambi questi numeri vengono stampati. #include <stdio.h> int main() { int prev, curr, temp; int n; int i; prev = 1; curr = 1; printf("Dammi un numero:"); scanf("%d", &n); for (i = 2; i < n; i++) { temp = curr + prev; prev = curr; curr = temp; } printf("Il %do numero di Fibonacci e': %d\n", n, temp); // estensione // cerca il primo numero di fibonacci piu' grande di n prev = 1; curr = 1; temp = 0; for (i = 2; temp < n; i++) { temp = curr + prev; prev = curr; curr = temp; } if ((curr - n) == (n - prev)) printf("Il numero e' equidistante da due numeri di Fibonacci: %d, %d\n", prev, curr); else { if ((curr - n) < (n - prev)) printf("Il numero di Fibonacci piu' vicino e': %d\n", curr); else printf("Il numero di Fibonacci piu' vicino e': %d\n", prev); } return 0; } Nota: la soluzione proposta non fa uso di strutture contenitori. Questo significa che nella parte che corrisponde all’estensione si ripetono calcoli che sono stati già svolti in precedenza. È possibile progettare una variante dell’esercizio che utilizza un array per immagazzinare i numeri della sequenza e che quindi svolge meno calcoli. Esercizio 4 (Battaglia navale) Si sviluppi una versione semplificata di un programma per giocare alla battaglia navale. Il programma presuppone l’esistenza di un unico giocatore. Lo schema e' prefissato, non cambia da partita a partita. Si distinguono due campi di battaglia: "schema" contiene la disposizione delle navi sul campo di battaglia; “battaglia" tiene traccia dei colpi effettuati dall'utente (se il colpo ha dato come risultato 'mancato' o 'colpito'). I simboli usati sono: 'x' = elemento di una nave 'o' = "mancato" '*' = "colpito" '.' = casella sulla quale non e' stato effettuato nessun colpo Il programma visualizza per il giocatore il contenuto di “battaglia”. Il giocatore inserisce l’informazione sul punto in cui vuole sparare utilizzando il tipico sistema di coppie <lettera, numero>. Il programma verifica se il colpo è andato a segno o no e lo comunica all’utente. */ #include <stdio.h> #define DIM_OR 6 #define DIM_VERT 6 typedef char matrice_caratteri[DIM_OR][DIM_VERT]; void main() { /* gli array "schema" e "battaglia" vengono inizializzati al momento della dichiarazione. Essendo questi degli array di caratteri, la sintassi per l'inizializzazione e' quella vista sotto, con sequenze di caratteri (comprese tra "") separate da virgole e racchiuse tra parentesi graffe. Questa inizializzazione e' ammissibile solo per matrici di caratteri e solo in fase di dichiarazione */ /* matrice_caratteri schema = {" 12345", "Axx...", "B...x.", "Cx....", "D.xx..", "E....x"}; matrice_caratteri battaglia = {" 12345", "A.....", "B.....", "C.....", "D.....", "E....."}; */ matrice_caratteri schema, battaglia; /* in alternativa e' possibile inizializzare la matrice schema con due cicli for innestati, che scorrano la matrice ed assegnino ad ogni cella il valore corretto */ char c1; int c2; int i, j; int pz_rim = 5; schema[0][0] = ' '; battaglia[0][0] = ' '; for(i=1; i<DIM_VERT; i++) { schema[0][i] = i+'1'-1; battaglia[0][i] = i+'1'-1; } for(i=1; i<DIM_OR; i++) { schema[i][0] = i+'A'-1; battaglia[i][0] = i+'A'-1; } for(i=1; i<DIM_OR; i++) for(j=1; j<DIM_VERT; j++) { schema[i][j]='.'; battaglia[i][j]='.'; } /* distribuzione delle navi */ schema[1][1]='x'; schema[1][2]='x'; schema[2][4]='x'; schema[3][1]='x'; schema[4][2]='x'; schema[4][3]='x'; schema[5][5]='x'; while(pz_rim > 0) { for( i=0 ; i<DIM_OR ; i++){ for( j=0 ; j<DIM_VERT ; j++) printf("%c", battaglia[i][j]); printf("\n"); } printf("\n* = colpito, o = mancato, . = acqua\n"); printf("Flotta disponibile 5 navi. Composizione navi: xx xx x x x\n"); printf("Scegli coordinate (lettera, numero) a cui sparare: "); scanf("%c%d", &c1, &c2); fflush(stdin); if(c1>='A' && c1<='E' && c2>=1 && c2<=5 && battaglia[c1-'A'+1][c2] == '.') { if(schema[c1-'A'+1][c2] == 'x'){ battaglia[c1-'A'+1][c2] = '*'; pz_rim--; printf("\n***COLPITO!***\n\n"); } else { battaglia[c1-'A'+1][c2] = 'o'; printf("\nMANCATO!\n\n"); } } } printf("\n******************\n"); printf("***HAI VINTO!!!***\n"); printf("******************\n"); } Esercizio 5. (TDE 18/11/2008) Per ognuna delle espressioni logiche riportate nelle righe della tabella, assumendo le seguenti dichiarazioni: int a = 8, b = 11; char c=’d’; indicare se l’espressione è vera o falsa (scrivere V o F nella seconda colonna). Indicare inoltre, nella terza colonna, se l’espressione è vera per qualsiasi valore delle variabili (scrivere SI o NO) e, nella quarta colonna, se l’espressione è falsa per qualsiasi valore delle variabili (scrivere SI o NO). Si giustifichino le risposte. Risposte prive di giustificazione non saranno prese in considerazione. Espressione Vera o falsa? (-a==a) && (a < 11) (c>’a’ || c<’z’ ) && (a<7 && b>8) !(b<10 && a>7) || (c!=’h’ && c>’a’ ) F F V Sempre vera? NO NO NO Sempre falsa? NO NO NO Giustificazioni 1. L’espressione è vera nel caso a=0 e falsa in tutti gli altri casi 2. L’espressione è falsa per i valori dati per la presenza del termine a<7; è vera, per valori di a<7 e b>8 perché il termine (c>’a’ || c<’z’) è sempre vero. Di conseguenza, l’espressione non è sempre falsa. 3. L’espressione è vera perché, indipendentemente dai valori di a e b, si ha che c!=’h’ e c>’a’ sono entrambe vere; è falsa, per esempio, per a=8, b=9 (che rendono falso il termine !(b<10 && a>7)) e c=’h’ (che rende falso il termine (c!=’h’ && c>’a’ )), quindi non è sempre vera. Esercizio 6. (programma misterioso TDE 18/11/2008) Si consideri il seguente programma C: #include <stdio.h> #define MAX 20 typedef int numeri[MAX]; int main() { numeri A; int p,j,i=0,s=0; scanf("%d",&A[i]); i++; while (i<MAX && A[i-1]>0) { scanf("%d",&A[i]); i++; } scanf("%d",&p); while (p<0||p>i) scanf("%d",&p); for (j=0;j<p;j++) s+=A[j]; for (j=p;j<i;j++) s-=A[j]; if (s==0) printf("%d",p); } else if (s<0) for (j=p;j<i;j++) printf("%d\n",A[j]); else for (j=0;j<p;j++) printf("%d\n",A[j]); 1. Si indichi cosa viene stampato a video se l’utente inserisce questa sequenza: 4 5 12 12 3 3 3 3 7 14 0 4. 2. Che cosa succede se l’utente invece di scrivere 4 come ultimo valore della sequenza scrive 3 ? Che cosa succede se invece scrive 12? 3. Spiegare brevemente il funzionamento del programma a seconda dei vari possibili ingressi. Soluzione punto 1 Il programma legge da tastiera una sequenza di numeri fino ad incontrare il valore 0 (o, più in generale, fino ad incontrare un valore minore o uguale a zero oppure a considerare 20 numeri). Poi legge un ulteriore valore (nell’esempio il valore 4) che viene usato per dividere la sequenza di numeri letta in precedenza in due porzioni distinte (nell’esempio, i primi 4 numeri della sequenza e gli altri). Il valore letto deve essere un indice ammissibile per la sequenza dei numeri, e, di conseguenza, il suo valore deve essere compreso tra zero ed i, dove i indica il numero di elementi letti in precedenza. A questo punto il programma calcola la differenza tra la somma dei numeri della prima porzione della sequenza e la somma dei numeri della seconda porzione della sequenza. Se tale differenza è pari a zero, il programma stampa il valore che è stato utilizzato per dividere la sequenza in due parti (il valore 4 nel nostro esempio). Se la differenza è minore di zero il programma stampa tutti i valori nella seconda porzione della sequenza. Se è maggiore di zero, il programma stampa tutti i valori nella prima porzione della sequenza. Nel caso dell’esempio specifico, la differenza è pari a zero e, di conseguenza, viene stampato il valore 4. Soluzione punto 2 Nel caso in cui l’utente inserisce 3 come ultimo elemento, il programma divide la sequenza nelle due sottosequenze composte dai numeri {4, 5, 12} la prima e dai numeri {12, 3, 3, 3, 3, 7, 14, 0} la seconda. In questo caso quindi la differenza calcolata risulta pari a -24. Di conseguenza, il programma stamperà a video i numeri 12 3 3 3 3 7 14 0. Nel caso in cui l’utente inserisce come ultimo valore il numero 12, la condizione del ciclo che si occupa dell’acquisizione dell’ultimo valore risulterà essere vera perché il termine p>=i sarà vero. Di conseguenza, il programma, rimarrà in attesa di ricevere un nuovo valore. Soluzione punto 3 Si veda la spiegazione al punto 1. Esercizio 7. (Venditori di auto TDE 14/11/2005) Le seguenti dichiarazioni di tipo servono per definire l’archivio di auto vendute da un concessionario. Per ogni auto, il concessionario tiene traccia del tipo, del numero di telaio, della data di vendita e della lista di optional istallati. Si scriva il frammento di programma che stampa a video la data di vendita di tutte le auto nell’archivio a che nella lista di optional hanno quello con numeroSerie pari a 412. Si assuma che i dati contenuti nella variabile a siano già stati tutti immessi (in un modo che non viene descritto). #define MAX_AUTO_VENDIBILI 1000 typedef char stringa[50]; typedef struct{ int giorno; int mese; int anno; }Data; typedef struct{ stringa tipo; int numeroSerie; }Optional; typedef struct{ stringa tipo; /* il tipo di auto */ int numeroTelaio; Data dataVendita; Optional listaOptional[10]; int numeroOptional; /* il numero effettivo di optional di un’auto */ } AutoVenduta; typedef struct{ AutoVenduta archAuto[MAX_AUTO_VENDIBILI]; int numAutoVendute; /* numero di auto vendute effettivamente dal concessionario */ }ArchivioAutoVendute; void main() { ArchivioAutoVendute a; // INSERIRE QUI LA SOLUZIONE } Soluzione Codice for (i=0; i<a.numAutoVendute; i++) for (j=0; j<a.archAuto[i].numeroOptional; j++) if (a.archAuto[i].listaOptional[j].numeroSerie == 412) printf("Auto venduta il %d %d %d", a.archAuto[i].dataVendita.giorno, a.archAuto[i].dataVendita.mese, a.archAuto[i].dataVendita.anno);