Lettura e Scrittura di Numeri in Virgola Mobile in Linguaggio C
In questa lezione vedremo come stampare valori in virgola mobile su console e come leggerli da tastiera in linguaggio C.
Vedremo come applicare la funzione printf
per scrivere valori float
, double
e long double
e la funzione scanf
per leggerli da tastiera.
printf
e tipi in virgola mobile
Adesso è giunto il momento di entrare nel dettaglio di come stampare su console valori di tipo double
, float
e long double
.
Rispetto ai tipi interi, i tipi in virgola mobile richiedono una formattazione più complessa. Ma partiamo con ordine.
Come abbiamo già visto in precedenza, per stampare valori di tipo differente attraverso la funzione printf
è necessario specificare nella stringa di formato dei cosiddetti specificatori di formato.
Nel caso dei numeri in virgola mobile esistono ben 3 specificatori.
Specificatori di Formato per Numeri in Virgola Mobile
Gli specificatori di formato per i numeri in virgola mobile sono:
%f
: stampa il numero in notazione decimale mantenendo al massimo 6 cifre decimali dopo il punto;%e
: stampa il numero in notazione scientifica mantenendo al massimo 6 cifre significative;%g
: stampa il numero in notazione decimale se possibile con 6 cifre significative, altrimenti utilizza la notazione scientifica.
Esaminiamo questi specificatori nel dettaglio.
Specificatore f
Lo specificatore %f
viene adoperato per stampare un valore in notazione decimale. In altre parole, il numero viene stampato con la virgola e la parte frazionaria.
Vediamo un esempio:
float pi = 3.14159;
printf("Il valore di pi è: %f\n", pi);
Il risultato sarà:
Il valore di pi è: 3.141590
Di default lo specificatore %f
stampa 6 cifre decimali. Pertanto, potrebbe capitare che il numero venga troncato in fase di stampa. Ad esempio:
float valore = 0.000123456;
printf("Il valore è: %f\n", valore);
Il risultato sarà:
Il valore è: 0.000123
Come si può vedere, il numero è stato troncato a 6 cifre decimali. Questo problema non si pone con la parte intera. Infatti, ad esempio se proviamo a stampare il numero 123456789.123456789
:
float valore = 123456789.123456789;
printf("Il valore è: %f\n", valore);
Il risultato sarà:
Il valore è: 123456789.123457
Da notare che la parte intera è stata stampata nella sua interezza. Viceversa, la parte frazionaria è stata troncata a 6 cifre decimali e arrotondata alla sesta cifra.
Per ovviare a questo problema, il linguaggio C mette a disposizione un altro specificatore di formato per stampare in notazione decimale.
Ricapitolando:
Specificatore f
per la funzione printf
Lo specificatore %f
indica alla funzione printf
di stampare un numero in virgola mobile in notazione decimale.
Il risultato avrà il seguente aspetto:
<parte intera>.<parte frazionaria>
Nel dettaglio, il suo comportamento di default è il seguente:
- La parte intera viene stampata nella sua interezza
- La parte frazionaria viene troncata e arrotondata a 6 cifre decimali.
Specificatore e
Lo specificatore %e
viene adoperato per stampare un valore in notazione scientifica. Quindi il numero sarà sempre rappresentato con un numero frazionario ed un esponente. Il numero frazionario avrà sempre una cifra diversa da zero prima del punto e l'esponente sarà modificato di conseguenza.
Ad esempio, se prendiamo il numero
float pi = 3.14159;
printf("Il valore di pi è: %e\n", pi);
Il risultato sarà:
Il valore di pi è: 3.141590e+00
Osserviamo alcune cose:
- Adoperando la notazione scientifica, il numero dovrebbe essere scritto come:
. In C, come in molti altri linguaggi, il viene omesso, in quanto implicito, e sostituito da e
. Pertanto, il numero viene scritto come3.141590e+00
; - il numero
già possiede una cifra diversa da zero prima del punto. Motivo per cui l'esponente è 0.
Proviamo a stampare un numero come 0.000123456
, quindi un numero con una cifra prima del punto pari a zero:
float valore = 0.000123456;
printf("Il valore è: %e\n", valore);
Il risultato sarà:
Il valore è: 1.234560e-04
Come si può vedere, il numero è stato stampato in notazione scientifica con l'esponente -4 e la cifra diversa da zero prima del punto.
Analogamente, se proviamo a stampare il numero 123.456789
:
float valore = 123.456789;
printf("Il valore è: %e\n", valore);
Il risultato sarà:
Il valore è: 1.234568e+02
Il punto è stato spostato di 2 posizioni a sinistra e l'esponente è stato incrementato di 2.
In ogni caso, e lo possiamo notare dall'ultimo esempio, il numero verrà stampato sempre arrotondato a 6 cifre significative.
Ricapitolando:
Specificatore e
per la funzione printf
Lo specificatore %e
indica alla funzione printf
di stampare un numero in virgola mobile in notazione scientifica.
Il risultato avrà il seguente aspetto:
<parte frazionaria>e<esponente>
Nel dettaglio, il suo comportamento di default è il seguente:
- La parte frazionaria viene troncata e arrotondata a 6 cifre significative;
- L'esponente viene calcolato in modo tale che la parte frazionaria abbia una sola cifra diversa da zero prima del punto.
Specificatore g
Lo specificatore %g
stampa i numeri in un formato ibrido tra %f
e %e
. Questo specificatore di formato sceglie automaticamente tra %f
e %e
in base al valore da stampare.
Nel dettaglio, lo specificatore %g
cerca di mantenere sempre 6 cifre significative. Se il numero può essere rappresentato con sole 6 cifre significative, verrà stampato in notazione decimale. Altrimenti, verrà stampato in notazione scientifica.
Ad esempio, se proviamo a stampare il numero
double pi = 3.14159;
printf("Il valore di pi è: %g\n", pi);
Il risultato sarà:
Il valore di pi è: 3.14159
Se proviamo a stampare il numero 0.000123456
invece:
double valore = 0.000123456;
printf("Il valore è: %g\n", valore);
Il risultato sarà:
Il valore è: 1.234560e-04
Mentre per printf
ha scelto di stampare il numero in notazione scientifica.
Analogamente, se proviamo a stampare il numero 123456789.123456789
:
double valore = 123456789.123456789;
printf("Il valore è: %g\n", valore);
Il risultato sarà:
Il valore è: 1.23457e+08
Anche in questo caso è stata usata la notazione scientifica.
Ricapitolando:
Specificatore g
per la funzione printf
Lo specificatore %g
indica alla funzione printf
di stampare un numero in virgola mobile secondo le seguenti regole:
- Se il numero può essere rappresentato con 6 cifre significative o meno in notazione decimale, allora verrà stampato in notazione decimale;
- Altrimenti, verrà stampato in notazione scientifica.
Modificare il numero di cifre decimali da stampare
Come abbiamo visto, i tre specificatori di formato %f
, %e
e %g
stampano al massimo 6 cifre decimali o 6 cifre significative.
Questo comportamento, ovviamente, non va bene per tutti i casi. Per fortuna, la funzione printf
mette a disposizione un modo per modificare il numero di cifre decimali da stampare.
La sintassi generale per formattare i numeri in virgola mobile è la seguente:
%[flags][dimesione][.precisione]{f|e|g}
Nella sintassi di sopra gli elementi f
, e
e g
sono obbligatori e funzionano come descritto sopra.
Gli altri elementi sono descritti di seguito:
-
dimensione
: specifica la quantità minima di caratteri con cui stampare il numero. Se il numero ha bisogno di meno caratteri, verrà riempito con spazi. Se il numero ha bisogno di più caratteri, verrà stampato con tutti i caratteri necessari. Da notare che nel computo dei caratteri vanno considerati anche il punto, il segno e l'esponente.Ad esempio, se proviamo a stampare il numero
con una dimensione di 10 caratteri: double pi = 3.14159; printf("Il valore di pi è: %10f\n", pi);
Il risultato sarà:
Il valore di pi è: 3.141590
Come si può notare, è stato aggiunto uno zero per raggiungere le 6 cifre decimali. Così facendo, il numero ha bisogno di 8 caratteri per essere stampato, incluso il punto. Per cui, sono stati aggiunti 2 spazi per raggiungere i 10 caratteri.
Se, invece, proviamo a stampare un numero come
123456789.123456789
con una dimensione di 10 caratteri:double valore = 123456789.123456789; printf("Il valore è: %10f\n", valore);
Il risultato sarà:
Il valore è: 123456789.123457
Per prima cosa, il numero è stato troncato a 6 cifre decimali, come indicato dallo specificatore
%f
. In questo modo il numero ha bisogno di 15 caratteri per essere stampato che superano il valore di 10. Per cui, il numero è stato stampato con tutti i caratteri necessari.Quindi se servono più caratteri di quelli specificati, la dimensione verrà ignorata.
-
precisione
: specifica il numero di cifre da inserire dopo il punto per gli specificatorif
ee
. Mentre, per lo specificatoreg
indica il numero di cifre significative da usare. Da notare che il punto va sempre inserito prima della precisione.Ad esempio, se proviamo a stampare il numero
con una precisione di 2 cifre decimali: double pi = 3.14159; printf("Il valore di pi è: %.2f\n", pi);
Il risultato sarà:
Il valore di pi è: 3.14
Come si può notare, il numero è stato troncato a 2 cifre decimali.
Se proviamo a stampare il numero
123456789.123456789
con una precisione di 4 cifre decimali:double valore = 123456789.123456789; printf("Il valore è: %.4f\n", valore);
Il risultato sarà:
Il valore è: 123456789.1235
Se avessimo, invece, adoperato lo specificatore
g
:double valore = 123456789.123456789; printf("Il valore è: %.4g\n", valore);
Il risultato sarebbe il seguente:
Il valore è: 1.235e+08
In questo caso è stata usata la notazione scientifica ed il numero è stato troncato a 4 cifre significative.
-
flags
: è un insieme di caratteri che possono essere utilizzati per modificare la formattazione del numero. Ne esistono 3 di flags.+
: Indica che il segno del numero deve essere sempre stampato. Anche se il numero è positivo.-
: Indica che il numero deve essere allineato a sinistra. Di default, i numeri sono allineati a destra.0
: Indica che i numeri devono essere riempiti con zeri. Di default, i numeri sono riempiti con spazi.
Vediamo qualche esempio. Se proviamo a stampare il numero
con il segno sempre stampato: double pi = 3.14159; printf("Il valore di pi è: %+f\n", pi);
Il risultato sarà:
Il valore di pi è: +3.141590
L'allineamento a sinistra, invece, ha senso se utilizziamo la dimensione. Ad esempio, se proviamo a stampare il numero
con una dimensione di 10 caratteri, allineato a sinistra e con sole 2 cifre decimali: double pi = 3.14159; printf("Il valore di pi è: %-10.2fcontinuo della riga\n", pi);
Il risultato sarà:
Il valore di pi è: 3.14 continuo della riga
Come si può notare, il numero è stato allineato a sinistra e riempito con spazi.
Infine, se proviamo a stampare il numero
con una dimensione di 10 caratteri, riempito con zeri e con 4 cifre decimali: double pi = 3.14159; printf("Il valore di pi è: %010.4f\n", pi);
Il risultato sarà:
Il valore di pi è: 000003.1416
In questo caso al posto degli spazi a sinistra, sono stati inseriti degli zeri.
Ricapitolando:
Formattazione dei Numeri in Virgola Mobile
La formattazione dei numeri in virgola mobile avviene attraverso la stringa di formato della funzione printf
secondo la seguente sintassi:
%[flags][dimesione][.precisione]{f|e|g}
Dove:
flags
: indica eventuali modifiche alla formattazione del numero. I flags disponibili sono:+
: indica che il segno del numero deve essere sempre stampato;-
: indica che il numero deve essere allineato a sinistra;0
: indica che i numeri devono essere riempiti con zeri ed ha senso solo se si specifica la dimensione;
dimensione
: specifica la quantità minima di caratteri con cui stampare il numero inclusi il punto, il segno e l'esponente;precisione
: specifica il numero di cifre decimali da stampare per gli specificatorif
ee
e il numero di cifre significative per lo specificatoreg
;
Forzare il tipo del valore in virgola mobile
Tutti gli specificatori appositi per i numeri in virgola mobile mostrati sinora effettuano l'implicita assunzione che il valore passato si di tipo double
.
Pertanto, anche se passiamo un valore float
oppure un long double
, internamente il compilatore provvederà a convertirlo in double
.
Se, invece, vogliamo forzare il tipo del valore passato, possiamo utilizzare i modificatori di tipo. Come nel caso degli interi, si tratta di apporre un prefisso davanti agli specificatori f
, e
o g
. Nel caso dei valori in virgola mobile esiste un solo prefisso:
L
: indica che il valore passato è di tipolong double
.
Quindi, ad esempio, se vogliamo stampare un valore long double
con 10 cifre decimali:
long double pi = 3.14159265358979323846L;
printf("Il valore di pi è: %.10Lf\n", pi);
Il risultato sarà:
Il valore di pi è: 3.1415926536
Esiste, tuttavia, il prefisso l
che viene però ignorato in quanto indica un valore di tipo double
che è il tipo di default.
Ricapitolando:
Forzare il Tipo del Valore in Virgola Mobile
Normalmente, i valori in virgola mobile passati alla funzione printf
vengono convertiti in double
.
Per forzare il tipo del valore in virgola mobile passato alla funzione printf
, è possibile utilizzare il prefisso L
davanti agli specificatori di formato f
, e
e g
.
Tale prefisso indica che il valore passato è di tipo long double
.
Lettura di valori in virgola mobile con scanf
Adesso che abbiamo visto come stampare valori in virgola mobile attraverso la funzione printf
è venuto il momento di vedere come leggere valori in virgola mobile.
Come per gli interi, possiamo adoperare la funzione scanf
utilizzando i corretti specificatori di formato.
In particolare, gli specificatori sono i seguenti:
%f
: per leggere un valore in virgola mobile di tipofloat
;%lf
: per leggere un valore in virgola mobile di tipodouble
;%Lf
: per leggere un valore in virgola mobile di tipolong double
.
Quando utilizziamo la scanf
per leggere un valore in virgola mobile, possiamo inserire il numero sia in notazione decimale che in notazione scientifica.
Ad esempio, se vogliamo leggere un valore double
:
#include <stdio.h>
int main() {
double valore;
printf("Inserisci un valore: ");
scanf("%lf", &valore);
printf("Hai inserito il valore: %lf\n", valore);
return 0;
}
Eseguendo il programma, possiamo inserire un numero sia in questo modo:
Inserisci un valore: 3.14159
Hai inserito il valore: 3.141590
Sia in questo modo:
Inserisci un valore: 1.234e-02
Hai inserito il valore: 0.012340
In ogni caso è di importanza fondamentale inserire il corretto specificatore adatto al tipo.
Notiamo alcune differenze fondamentali rispetto alla funzione printf
per quanto riguarda gli specificatori di formato:
- Non esistono tre specificatori diversi per i valori in virgola mobile per la funzione
scanf
a seconda della notazione. Quindi,%f
legge sia valori in notazione decimale che in notazione scientifica; - A differenza della funzione
printf
, nel caso dellascanf
lo specificatore%f
legge unfloat
e non undouble
. Per leggere undouble
è necessario utilizzare lo specificatore%lf
.
Ricapitolando:
Lettura di Valori in Virgola Mobile con scanf
Per leggere valori in virgola mobile con la funzione scanf
è necessario utilizzare i seguenti specificatori di formato:
%f
: per leggere un valore in virgola mobile di tipofloat
;%lf
: per leggere un valore in virgola mobile di tipodouble
;%Lf
: per leggere un valore in virgola mobile di tipolong double
.
Specificatore di formato %f
Lo specificatore di formato %f
rappresenta uno di quei rari casi in cui il tipo di dato passato alla funzione scanf
è diverso da quello passato alla funzione printf
.
Infatti, mentre %f
per la funzione printf
rappresenta un double
, per la funzione scanf
rappresenta un float
.
In Sintesi
In questa lezione abbiamo visto come stampare e leggere valori in virgola mobile in linguaggio C.
Per stampare valori in virgola mobile, abbiamo utilizzato i tre specificatori di formato %f
, %e
e %g
. Abbiamo visto come modificare il numero di cifre decimali da stampare e come forzare il tipo del valore passato.
Il formato generale per formattare i numeri in virgola mobile è:
%[flags][dimesione][.precisione]{f|e|g}
dove:
flags
: indica eventuali modifiche alla formattazione del numero;dimensione
: specifica la quantità minima di caratteri con cui stampare il numero;precisione
: specifica il numero di cifre decimali da stampare per gli specificatorif
ee
e il numero di cifre significative per lo specificatoreg
.
Per leggere valori in virgola mobile, abbiamo utilizzato i specificatori %f
, %lf
e %Lf
per leggere rispettivamente float
, double
e long double
.