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.

Definizione

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:

Definizione

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 \pi:

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: 3.141590 \cdot 10^0. In C, come in molti altri linguaggi, il 10 viene omesso, in quanto implicito, e sostituito da e. Pertanto, il numero viene scritto come 3.141590e+00;
  • il numero \pi 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:

Definizione

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 \pi:

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 pi è stata usata la notazione decimale, il secondo numero è stato stampato in notazione scientifica. Questo perché esso non è rappresentabile con sole 6 cifre significative usando la notazione decimale. Per cui, la funzione 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:

Definizione

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 \pi 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 specificatori f e e. Mentre, per lo specificatore g 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 \pi 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.

    1. +: Indica che il segno del numero deve essere sempre stampato. Anche se il numero è positivo.
    2. -: Indica che il numero deve essere allineato a sinistra. Di default, i numeri sono allineati a destra.
    3. 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 \pi 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 \pi 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 \pi 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:

Definizione

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 specificatori f e e e il numero di cifre significative per lo specificatore g;

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 tipo long 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:

Definizione

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 tipo float;
  • %lf: per leggere un valore in virgola mobile di tipo double;
  • %Lf: per leggere un valore in virgola mobile di tipo long 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 della scanf lo specificatore %f legge un float e non un double. Per leggere un double è necessario utilizzare lo specificatore %lf.

Ricapitolando:

Definizione

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 tipo float;
  • %lf: per leggere un valore in virgola mobile di tipo double;
  • %Lf: per leggere un valore in virgola mobile di tipo long double.
Nota

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 specificatori f e e e il numero di cifre significative per lo specificatore g.

Per leggere valori in virgola mobile, abbiamo utilizzato i specificatori %f, %lf e %Lf per leggere rispettivamente float, double e long double.