• Non ci sono risultati.

4.2 Classificazione dei tipi di dato

4.2.1 Tipi di dati numerici

I tipi di dati numerici si dividono ulteriormente in due grandi categorie: i tipi interi e i cosiddetti tipi reali. Tutti i linguaggi di programmazione supportano i tipi di dati numerici con le usuali operazioni aritmetiche e di confronto.

Tipi interi

I tipi interi servono a rappresentare numeri interi. Poiché i numeri interi sono infiniti, sarà possibile rappresentarne solo un sottoinsieme finito.

La rappresentazione più usata è quella in complemento a due con un numero fissato N di bit. In tale rappresentazione un bit serve per indicare il segno del numero intero mentre il resto serve ad indicare il valore assoluto del numero (seppure in maniera diversa per i numeri positivi o nulli rispetto a quelli negativi).

Con N bit si possono rappresentare i numeri interi che vanno da -2N-1 a +2N-1-1.

Ad esempio con N=16 si possono rappresentare i numeri compresi tra -32.768 e +32.767, mentre con N=32 i numeri tra -2.147.483.648 e +2.147.483.647.

In C++ si distinguono interi corti (short int), interi (int) e interi lunghi (long int). Rientrano tra i tipi numerici anche i caratteri (char). Il numero di bit usati non è specificato nello standard, però è possibile ordinare i tipi in base al numero di bit

char ≤ short int ≤ int ≤ long int Le principali operazioni supportate in C++ sono

● operazioni aritmetiche +, -, *, /, %

● operazioni logiche sui bit <<, >>, &, |, ^, ~

● operazioni di confronto <, >, <=, >=, ==, !=

● funzione valore assoluto labs

L'operazione / restituisce il quoziente della divisione, quindi 7 / 2 fa 3 (e non 3.5 come in molti altri linguaggi), mentre l'operazione % restituisce il resto, quindi 7 % 2 fa 1. Esiste anche l'operatore unario – che restituisce come risultato l'opposto del proprio argomento, ad esempio -(-5) fa 5.

Le operazioni logiche operano sugli interi trattandoli come sequenze di bit:

● << effettua lo shift a sinistra,

● >> effettua lo shift a destra,

● & effettua l'operazione AND,

● | effettua l'operazione OR,

● ^ effettua l'operazione XOR,

● ~ effettua l'operazione NOT.

Le operazioni di confronto <= e >= corrispondono chiaramente a ≤ e ≥ rispettivamente, l'operatore == corrisponde all'uguaglianza (e non l'operatore =, che invece è l'assegnamento) e il != corrisponde alla disuguaglianza ≠.

Le costanti intere si possono inserire, oltre che in base 10, anche in base 8 o in base 16. Per usare la base 8 il numero deve iniziare per 0 e contenere solo cifre tra 0 e 7. Ad esempio 013 è il numero decimale 11. Per usare la base 16 il numero deve iniziare con 0x o 0X e contenere cifre tra 0 e 9 e tra A e F (anche in minuscolo). Le cifre tra A e F hanno valori da 10 a 15. Ad esempio 0x2A è il numero decimale 42.

L. Ad esempio la costante 5L è di tipo long, mentre 5 è di tipo int.

In C++ è possibile anche usare numeri naturali, ovvero positivi o nulli, con i tipi unsigned int,

unsigned short e unsigned long. Con N bit si possono rappresentare i numeri interi che

vanno da 0 a +2N-1.

Ad esempio con N=16 si possono rappresentare i numeri compresi tra 0 e +65.535.

Analogamente a quanto visto per le costanti di tipo long, le costanti di tipo unsigned int hanno il suffisso U. Ad esempio la costante 5U è di tipo unsigned int.

Tipi reali

I tipi reali servono a rappresentare quelli che in matematica sono chiamati numeri razionali. In particolare saranno rappresentati solo un sottoinsieme finito. La rappresentazione più utilizzata è quella in base due a virgola mobile. Una parte dei bit a disposizione, detta mantissa, serve a rappresentare le cifre complessive del numero (sia quelle prima che quelle dopo la virgola), un bit della mantissa indica il segno, mentre la parte restante, detta esponente, indica la posizione della virgola all'interno del numero.

Il dominio dei tipi reali è un po' complicato da descrivere e si rimanda ad un testo specialistico per una sua descrizione dettagliata. Possiamo comunque dire che il dominio nella parte positiva è limitato superiormente da un numero massimo M ed inferiormente da un numero minimo m e analogamente nella parte negativa è compreso tra -M e -m. Essendo finito il dominio, non tutti i numeri reali in tali intervalli sono rappresentabili esattamente, gli altri possono essere rappresentati in maniera approssimata.

Le operazioni supportate in C sono

● operazioni aritmetiche +, -, *, /

● operazioni di confronto <, >, <=, >=, ==, !=

● funzioni matematiche fabs, pow, sqrt, ceil, floor, sin, cos, tan, exp, log, ... L'operazione di divisione non produce il resto: ad esempio 7.0/2.0 fa 3.5.

La funzione fabs calcola il valore assoluto, sqrt la radice quadrata, sin il seno, exp la funzione esponenziale ex, log il logaritmo naturale, ecc. Tali funzioni hanno un argomento che deve indicato

tra parentesi tonde. Ad esempio sqrt(2.0) calcola la radice quadrata di 2.

La funzione pow ha due argomenti x e y e calcola l'elevamento a potenza xy. Ad esempio pow(3.1,

5.0) calcola 3.15.

La funzione floor(x) restituisce la parte intera di x, ovvero il numero intero inferiore o uguale a x, ad esempio floor(3.7) fa 3.0, mentre ceil restituisce il numero intero superiore o uguale a x ad esempio ceil(3.7) fa 4.0.

Le costanti di tipo reale possono essere introdotte in due modi: la notazione standard e la notazione scientifica. La notazione standard prevede che il numero sia scritto secondo la grammatica

costante_reale_standard ::= [“+” | ”-”] sequenza_cifre “.” sequenza_cifre sequenza_cifre ::= cifra { cifra }

Si noti che il punto e la parte decimale sono obbligatorie anche se il numero è intero. Ad esempio 3.14 o -7.0.

La notazione scientifica serve a scrivere in maniera compatta numeri molto grandi o molto piccoli, sia positivi che negativi. La grammatica di tale notazione è

Il numero è espresso mediante il prodotto tra la costante reale e 10 elevato alla costante intera. Ad esempio 1.3e-4 corrisponde 1.3 10-4, mentre -1.2e+9 corrisponde -1.2 109.

Conversione di tipo

La conversione di un dato da un tipo di dato ad un altro è di solito un'operazione impossibile. Tra i casi in cui ciò ha senso vi sono quelli in cui si vuole convertire un dato numerico in un altro tipo numerico.

I tipi di dato numerici formano una gerarchia simile a quanto si riscontra nella matematica. In C++ si ha

short int ≤ int  long int  float  double

ove con il simbolo T1 T2 intendiamo denotare che T1 è un sottotipo di T2. Essere sottotipo comporta che la conversione di un dato dal tipo T1 al tipo T2 è agevole: ad esempio è facile convertire un numero int in un numero long int. Dato che tale conversione è sempre possibile e produce un risultato corretto, in C++ quando è necessario è fatta automaticamente (tramite opportune istruzioni in L.M. inserite dal compilatore). Ad esempio nell'espressione 4+3.1 il numero int 4 è convertito in float prima di effettuare l'addizione.

La conversione da un tipo numerico ad un suo sottotipo è invece un'operazione che potrebbe essere impossibile o far perdere informazione. Ad esempio è impossibile convertire esattamente il numero 123456789 al tipo short int (a 16 bit), in quanto il massimo intero rappresentabile in quest'ultimo tipo di dato è 32767. Mentre la conversione da double a float fa perdere le ultime cifre decimali: convertendo il numero 4.25167536214367 in float produce il numero 4.25168.

Perciò in C++ tali conversioni devono essere richieste esplicitamente nel programma mediante gli operatori di cast. Il modo più semplice, valido anche per il C, è quello di anteporre al dato il tipo (scritto tra parentesi) a cui lo si vuole convertire. Ad esempio per convertire 2.0 in int si scrive (int) 2.0. Il risultato sarà il numero intero 2.

La conversione di tipo è possibile anche in altri casi, ad esempio con i puntatori, come si vedrà nel capitolo 13.

Altri tipi numerici

Alcuni linguaggi permettono di usare anche i numeri complessi. In C++ questo è possibile tramite la libreria standard complex.

Nelle applicazioni finanziare è essenziale lavorare con decimi e centesimi in maniera esatta ed è quindi necessario rappresentare i numeri reali in base 10 (tipo di dato decimal, presente in qualche linguaggio).

Infine in alcuni linguaggi è possibile usare numeri naturali arbitrariamente grandi (interi multiprecisione). Ad esempio in Java esiste il tipo di dato bignum.

Documenti correlati