Numeri interi in MATLAB
Nelle lezioni precedenti abbiamo visto come MATLAB usa i numeri floating point per rappresentare i numeri reali. In particolare, abbiamo osservato il tipo double
e il tipo single
. In più abbiamo introdotto i numeri complessi attraverso la funzione complex
e come MATLAB utilizzi ben due numeri reali per rappresentare un numero complesso.
In questa lezione vedremo come in matlab si possono usare i numeri interi.
Numeri interi
Nella lezione in cui ho introdotto i tipi in virgola mobile abbiamo visto che, di default, MATLAB utilizza il double
per rappresentare i numeri. Questo tipo utilizza 64 bit, o 8 byte, per rappresentare un numero reale. Nella stessa lezione ho poi parlato del tipo single
che occupa la metà in termini di spazio e, tipicamente, richiede meno tempo per i calcoli. In poche parole, anche se il tipo di default è il double
, quando non è richiesta la precisione offerta, è sufficiente utilizzare il single
in maniera da ottimizzare le risorse impiegate nei nostri calcoli.
Analogamente, anche se il tipo di default è il double
, se nei nostri calcoli si ha a che fare con numeri interi è possibile ottimizzare ancor di più il tempo e la memoria impiegati dai nostri calcoli utilizzando i tipi interi.
I tipi interi sono forniti da quasi tutti i linguaggi di programmazione e, a livello hardware, i calcoli che il processore effettua su di essi impiegano meno tempo. MATLAB offre ben otto tipi di interi diversi che sono riportati nella tabella seguente:
Tipo | Intervallo di rappresentazione | Descrizione |
---|---|---|
int8 |
Interi con segno a 8 bit | |
int16 |
Interi con segno a 16 bit | |
int32 |
Interi con segno a 32 bit | |
int64 |
Interi con segno a 64 bit | |
uint8 |
Interi senza segno a 8 bit | |
uint16 |
Interi senza segno a 16 bit | |
uint32 |
Interi senza segno a 32 bit | |
uint64 |
Interi senza segno a 64 bit |
Dalla tabella precedente possiamo notare che i numeri interi possono essere divisi in due gruppi da 4, il primo composto dai numeri interi con segno e il secondo composto dai numeri interi senza segno. I tipi appartenenti al primo gruppo possono essere impiegati per rappresentare numeri negativi, mentre i secondi soltanto per rappresentare numeri positivi compreso lo zero. Il vantaggio di usare i numeri senza segno sta nel fatto che è possibile rappresentare numeri positivi più grandi rispetto agli interi con segno.
Il modo più semplice per ricordare a memoria il nome di questi tipi è quello di usare int
, che sta per integer o intero, e porgli davanti una u
se vogliamo rappresentare numeri senza segno (la u
sta infatti per unsigned
, i.e. senza segno) e porre come suffisso il numero di bit da usare: 8, 16, 32 o 64.
Creare una variabile o un valore intero è molto semplice. Basta utilizzare il nome del tipo e inserire tra parentesi il valore da rappresentare. Ad esempio, se vogliamo creare una variabile di tipo int8
con valore 15 basta inserire nel prompt il seguente comando:
>> x = int8(15)
x =
int8
15
Se poi usiamo il comando whos
per avere informazioni sulla nostra variabile, il risultato sarà:
>> whos
Name Size Bytes Class Attributes
x 1x1 1 int8
Come si può vedere, la nostra variabile x
occupa soltanto un byte, ossia 8 bit: otto volte meno rispetto ad un double
.
In realtà, nel caso precedente, int8
rappresenta una funzione a tutti gli effetti. Essa prende in ingresso un valore, una variabile o un'espressione e la converte in un intero a 8 bit con segno. Ad esempio possiamo eseguire il seguente comando:
>> int16(5 + 3 * 2)
ans =
int16
11
Nell'esempio precedente, l'intera espressione
Usare i tipi interi
MATLAB, di default, memorizza i valori numerici usando il tipo double
. Per poter utilizzare i tipi interi bisogna utilizzare esplicitamente le funzioni di conversione che hanno lo stesso nome del tipo che si vuole usare:
int8
int16
int32
int64
uint8
uint16
uint32
uint64
Queste funzioni prendono in ingresso un'espressione e la trasformano nel tipo desiderato.
Arrotondamento
Nell'invocare una delle otto funzioni di creazione interi, se il risultato dell'espressione passata come argomento è un numero con parte frazionaria, MATLAB provvede in automatico a convertire il valore nel più vicino intero. Ad esempio:
>> int16(3.6)
ans =
int16
4
>> int16(3.2)
ans =
int16
3
Nel primo caso,
>> int16(-3.5)
ans =
int16
-4
>> int16(3.5)
ans =
int16
4
Nel primo caso, infatti, per il valore
Questo funzionamento, in gergo tecnico, prende il nome di arrotondamento o rounding ed è un tema abbastanza delicato che affronteremo nelle lezioni di calcolo numerico.
Arrotondamento di default
Se non specificato diversamente, MATLAB utilizza uno schema di arrotondamento di default.
In base a questo schema, un numero reale viene convertito nel numero intero più vicino. Nel caso in cui esistono due numeri interi ad uguale distanza, ossia quando la parte frazionaria è pari a
MATLAB permette di scegliere altri schemi di arrotondamento diversi da quello riportato sopra.
Funzioni intmax
e intmin
Analogamente al caso dei numeri double
per cui esistono le funzioni realmin
e realmax
, anche per i numeri interi esistono due funzioni che ci permettono di ricavare il minimo intero rappresentabile e il massimo intero rappresentabile: intmin
e intmax
.
Queste due funzioni richiedono in ingresso il nome del tipo racchiuso tra apici e restituiscono l'intero massimo o minimo rappresentabile per quel tipo.
Ad esempio, sappiamo che il tipo uint16
è in grado di rappresentare numeri interi compresi tra
>> intmin('uint16')
ans =
uint16
0
>> intmax('uint16')
ans =
uint16
65535
Come si può osservare dall'esempio, i risultati combaciano con quanto ci aspettavamo.
Minimo intero e Massimo intero rappresentabili
Le funzioni intmin
e intmax
permettono di ottenere, rispettivamente, il minimo intero e il massimo intero rappresentabile con un tipo.
Queste funzioni richiedono in ingresso il nome del tipo tra apici singoli e restituiscono il valore nel tipo richiesto
intmax('nome-tipo');
intmin('nome-tipo');
Operazioni sui numeri interi
Quando utilizziamo numeri interi, rispetto ai numeri in virgola mobile, bisogna fare attenzione alle operazioni matematiche che applichiamo ad essi.
L'insieme dei numeri naturali unsigned
) o interi, per cui può capitare che il risultato di una somma o moltiplicazione tra due interi sia al di fuori di tale intervallo. In questi casi, MATLAB semplicemente, senza segnalare alcun errore, darà come risultato il massimo o minimo intero rappresentabile. Prendiamo l'esempio seguente:
>> intmax('uint8')
ans =
uint8
255
>> x = uint8(240)
x =
uint8
240
>> x + 50
ans =
uint8
255
Come possiamo vedere, il massimo intero rappresentabile da un uint8
è x
, che vale
Stesso discorso vale per la moltiplicazione:
>> intmax('uint16')
ans =
uint16
65535
>> x = uint16(4000)
x =
uint16
4000
>> x ^ 2
ans =
uint16
65535
In questo caso, la variabile x
, che è un uint16
e vale
Analogo discorso vale per la sottrazione, che è soddisfa la proprietà di chiusura per i numeri interi
>> intmin('int8')
ans =
int8
-128
>> x = int8(-100)
x =
int8
-100
>> x - 200
ans =
int8
-128
La divisione merita, invece, un discorso a parte. La divisione non soddisfa la proprietà di chiusura ne per i numeri interi ne per quelli naturali. In generale, quindi, il risultato di una divisione non necessariamente è un numero intero. In tal caso MATLAB restituirà l'arrotondamento del risultato, secondo le regole viste sopra:
>> x = int8(3)
x =
int8
3
>> x / 2
ans =
int8
2
Divisione per zero
Discorso a parte merita la divisione di un numero intero per zero in MATLAB. Infatti, un'operazione del genere porta ad un risultato inaspettato.
Come abbiamo visto, per i tipi floating point, double
e single
, una divisione per 0 restituisce il valore Inf
che in MATLAB sta ad indicare una quantità talmente grande da non poter essere rappresentata.
Generalmente, la maggior parte dei linguaggi di programmazione trattano la divisione per zero come un'errore e forniscono dei meccanismi per segnalare questo evento. In MATLAB ciò non avviene, per cui, in coerenza con quanto avviene con i numeri in virgola mobile, una divisione per zero restituisce, nel caso degli interi, il massimo intero rappresentabile al posto di Inf
.
Prendiamo l'esempio che segue:
>> x = int16(8);
>> y = int16(0);
>> x / y
ans =
int16
32767
Se x
e y
fossero stati due double
il risultato sarebbe stato Inf
che è, in un certo senso, un valore floating point che indica una quantità così grande da non poter essere rappresentata da un double
. Nel caso degli interi, questo comportamento corrisponde al restituire il massimo numero intero rappresentabile, che in questo caso è 32767
per gli interi a 16 bit int16
.
Del resto, se convertiamo Inf
in un numero intero otteniamo il seguente risultato:
>> int16(Inf)
ans =
int16
32767
Divisione per zero
In MATLAB, se nel dividere due numeri interi si verifica una divisione per zero non verrà segnalato nessun errore. Il risultato sarà il massimo intero rappresentabile.
Questo comportamento è in coerenza con il fatto che la divisione per zero di un numero in virgola mobile restituisce Inf
.
NaN
e numeri interi
Così come per la divisione per zero, anche la conversione del valore NaN
in un valore intero restituisce un risultato inaspettato.
Proviamo a vedere l'esempio che segue:
>> x = int16(0);
>> y = int16(0);
>> x / y
ans =
int16
0
Se x
e y
fossero stati due numeri in virgola mobile, il risultato sarebbe stato NaN
, ossia un valore indefinito. Nel caso dei numeri interi, un risultato del genere non è rappresentabile.
Per questo motivo, i progettisti di MATLAB hanno deciso di trasformare NaN
in zero quando si converte questo valore in un intero.
A prima vista, questa scelta può sembrare arbitraria. In realtà, la motivazione sta nel fatto che un valore indefinito può essere assimilato ad un valore logico falso. Nelle prossime lezioni vedremo le espressioni logiche in MATLAB, per il momento basti sapere che sono espressioni che hanno due possibili risultati: vero o falso. In generale in MATLAB qualsiasi valore diverso da 0 rappresenta il valore logico "vero", mentre lo zero rappresenta il "falso". Quindi, associare a NaN
il valore 0 è una scelta di coerenza con un'espressione logica "falsa".
Questo comportamento può essere verificato con i comandi che seguono:
>> int16(nan)
ans =
int16
0
NaN
e interi
In MATLAB, il valore indefinito NaN
viene convertito in zero quando si applica la conversione a numeri interi.
Operazioni tra interi di tipo diverso
Sebbene MATLAB metta a disposizione otto tipi diversi per i numeri interi, non è possibile combinare nella stessa espressione interi di tipo differente. Ad esempio, un'espressione del genere produce un errore:
>> x = int8(8);
>> y = int16(3);
>> x * y
Error using *
Integers can only be combined with integers of the same class, or scalar doubles.
>> x + y
Error using +
Integers can only be combined with integers of the same class, or scalar doubles.
L'unico modo di effettuare queste operazioni è di convertire i valori nello stesso tipo intero. Ritornando all'esempio di prima si potrebbe risolvere il tutto in questo modo:
>> x = int8(8);
>> y = int16(3);
>> x_16 = int16(x);
>> x_16 * y
ans =
int16
24
Espressioni con interi di tipo diverso
In MATLAB non è possibile combinare nella stessa espressione valori di tipo intero diverso.
L'espressione deve contenere solo interi dello stesso tipo.
Operazioni tra numeri interi e numeri reali
MATLAB permette di inserire espressioni che coinvolgono valori di tipi diversi. Quindi è possibile combinare, con un'espressione, valori double
, single
e complex
.
Analogamente, è possibile inserire nella stessa espressione valori in virgola mobile e valori interi. Il risultato in MATLAB è, tuttavia, differente rispetto a quanto accade in altri linguaggi di programmazione.
Ad esempio, in python, moltiplicando un numero in virgola mobile per un intero restituisce un numero in virgola mobile. Questo perché, nel caso in cui venisse restituito un intero si perderebbe precisione. Pensiamo a questo esempio:
Se il risultato fosse convertito in un intero, otterremmo
MATLAB, tuttavia, adotta un approccio differente. Infatti, quando si presenta un'espressione che coinvolge sia interi che valori in virgola mobile, MATLAB esegue i seguenti passaggi:
- Tutti i valori non in virgola mobile vengono convertiti in
double
; - Vengono effettuati i calcoli;
- Il risultato viene riconvertito, tramite arrotondamento, nel tipo intero utilizzato nell'espressione.
Ad esempio, inserendo i seguenti comandi:
>> x = 5.23;
>> y = int16(4);
>> x * y
ans =
int16
21
In questo esempio, il risultato corretto sarebbe:
Infine, espressioni che contengono numeri interi e numeri complessi non sono supportate.
Operazione tra numeri interi e valori in virgola mobile
In MATLAB, espressioni che contengono sia numeri interi che numeri in virgola mobile vengono gestite in questo modo:
- Tutti i valori sono convertiti in
double
; - Vengono eseguite le operazioni;
- Il risultato è arrotondato e convertito in intero.
Operazioni tra complex
e interi non sono supportate.
Quando usare i numeri interi
In questa lezione abbiamo visto le caratteristiche dei numeri interi. In particolare, abbiamo visto che per poterli usare è necessario specificare esplicitamente il tipo utilizzando le funzioni di conversione.
A questo punto sorge la domanda. Perché non utilizzare sempre i double
, ossia il tipo predefinito di MATLAB, nei nostri calcoli?
Esistono due importanti motivazioni per voler usare gli interi.
I calcoli con i numeri interi sono più veloci
Tipicamente i calcoli che coinvolgono numeri interi sono eseguiti molto più velocemente dal processore rispetto ai calcoli in virgola mobile.
Per questo motivo, se abbiamo a che fare con quantità intere in cui non abbiamo bisogno della parte frazionaria, conviene usare i numeri interi per velocizzare l'esecuzione dei nostri calcoli.
La seconda motivazione è la seguente:
Un valore double
non necessariamente è in grado di rappresentare un numero intero di grandi dimensioni con precisione assoluta
Un valore double
è in grado di rappresentare fino ad un massimo di 15 cifre significative.
Per comprendere questa affermazione, partiamo dalla considerazione che una variabile uint64
è in grado di rappresentare con precisione assoluta valori giganteschi. Infatti, con un uint64
siamo in grado di rappresentare numeri interi compresi tra
Con una variabile double
siamo in grado, in effetti, di rappresentare numeri fino a realmax('double') = 1.79769313486232e+308
, ossia fino a circa double
riesce a rappresentare fino a circa 15 o 16 cifre significative.
Effettuando una conversione di intmax('uint16')
in double
il risultato che otteniamo è il seguente:
>> intmax('uint64')
ans =
uint64
18446744073709551615
>> double(ans)
ans =
1.84467440737096e+19
Come si può osservare, le ultime cinque cifre del numero intero sono arrotondate. Per cui otteniamo che:
Quindi, quando i valori reali non sono necessari, è spesso conveniente usare i numeri interi.
In sintesi
In questa lezione abbiamo studiato i numeri interi. Abbiamo visto come definire un numero intero, come usarli in espressioni complesse.
MATLAB tratta i numeri interi in maniera differente ad altri linguaggi. Nella divisione per zero non segnala errori e quando vengono sforati i limiti di rappresentazione, i valori collassano nel massimo o minimo numero intero rappresentabile.
Inoltre, MATLAB, quando si hanno espressioni che combinano numeri interi e valori in virgola mobile restituisce sempre un valore intero.