Funzioni Matematiche di Arrotondamento e Resto in Linguaggio C

Concetti Chiave
  • La libreria matematica standard di C fornisce diverse funzioni per arrotondare numeri in virgola mobile secondo regole specifiche: ceil, floor, trunc, round, nearbyint e rint.
  • Le funzioni di arrotondamento possono restituire il risultato come un valore in virgola mobile o come un valore intero: lround e llround.
  • La libreria matematica standard di C fornisce anche funzioni per calcolare il resto della divisione tra numeri in virgola mobile: fmod, remainder e remquo.

Funzioni di Arrotondamento

La libreria matematica standard di C fornisce diverse funzioni per approssimare i numeri in virgola mobile.

Tutte queste funzioni sono dichiarate nell'header <math.h>.

Possiamo dividere queste funzioni in tre categorie:

  • Funzioni che arrotondano un numero in virgola mobile verso l'alto o verso il basso e lo restituiscono come un valore in virgola mobile:

    Funzione Descrizione Note
    double ceil(double x); Restituisce il più piccolo intero maggiore o uguale a x.
    double ceilf(float x); Versione float di ceil. C99
    double ceill(long double x); Versione long double di ceil. C99
    double floor(double x); Restituisce il più grande intero minore o uguale a x.
    double floorf(float x); Versione float di floor. C99
    double floorl(long double x); Versione long double di floor. C99
    Tabella 1: Funzioni ceil e floor della libreria matematica standard.

    La funzione ceil e le sue varianti restituiscono il più piccolo intero (in forma di double) che è maggiore o uguale al valore passato come argomento. Infatti il nome "ceil" deriva dalla parola inglese "ceiling", che significa "soffitto" o "parte superiore".

    Viceversa, la funzione floor e le sue varianti restituiscono il più grande intero (in forma di double) che è minore o uguale al valore passato come argomento. Il nome "floor" deriva dalla parola inglese "floor", che significa "pavimento" o "parte inferiore".

    Matematicamente, possiamo esprimere il comportamento di queste funzioni come segue:

    \left\lceil x \right\rceil = \min \{ n \in \mathbb{Z} \mid n \geq x \}
    \left\lfloor x \right\rfloor = \max \{ n \in \mathbb{Z} \mid n \leq x \}

    Ad esempio:

    • ceil(4.2) restituisce 5.0
    • ceil(-4.2) restituisce -4.0
    • floor(4.2) restituisce 4.0
    • floor(-4.2) restituisce -5.0

    In altre parole, la funzione ceil arrotonda sempre verso l'alto, mentre la funzione floor arrotonda sempre verso il basso.

  • Funzioni che arrotondano un numero in virgola mobile sempre verso lo zero e lo restituiscono come un valore in virgola mobile:

    Funzione Descrizione Note
    double trunc(double x); Restituisce la parte intera di x, arrotondata verso zero. C99
    double truncf(float x); Versione float di trunc. C99
    double truncl(long double x); Versione long double di trunc. C99
    Tabella 2: Funzioni trunc della libreria matematica standard.

    La funzione trunc e le sue varianti restituiscono la parte intera del numero passato come argomento, arrotondata sempre verso zero. Ad esempio:

    • trunc(4.7) restituisce 4.0
    • trunc(-4.7) restituisce -4.0

    In altre parole, la funzione trunc elimina la parte decimale del numero indipendentemente dal segno del numero stesso. Pertanto, trunc arrotonda sempre verso zero.

    Infatti, possiamo esprimere il comportamento di questa funzione come segue:

    \text{trunc}(x) = \begin{cases} \left\lfloor x \right\rfloor &amp; \text{se } x \geq 0 \\ \left\lceil x \right\rceil &amp; \text{se } x &lt; 0 \end{cases}
  • Funzioni che arrotondano un numero in virgola mobile lontano dallo zero e lo restituiscono come un valore in virgola mobile:

    Funzione Descrizione Note
    double round(double x); Restituisce il valore di x arrotondato all'intero più vicino. In caso di parità, arrotonda lontano da zero. C99
    double roundf(float x); Versione float di round. C99
    double roundl(long double x); Versione long double di round. C99
    Tabella 3: Funzioni round della libreria matematica standard.

    La funzione round e le sue varianti arrotondano il numero passato come argomento all'intero più vicino. In caso di parità (quando la parte decimale è esattamente 0.5), la funzione arrotonda lontano da zero. Ad esempio:

    • round(4.5) restituisce 5.0
    • round(4.4) restituisce 4.0
    • round(-4.5) restituisce -5.0
    • round(-4.4) restituisce -4.0

    In altre parole, la funzione round segue la regola dell'arrotondamento standard, ma in caso di parità, si assicura che il risultato sia sempre più lontano da zero.

    Infatti, possiamo esprimere il comportamento di questa funzione come segue:

    \text{round}(x) = \begin{cases} \left\lfloor x + 0.5 \right\rfloor &amp; \text{se } x \geq 0 \\ \left\lceil x - 0.5 \right\rceil &amp; \text{se } x &lt; 0 \end{cases}

Oltre a queste funzioni che arrotondano in base a regole specifiche, la libreria fornisce anche delle funzioni per arrotondare un numero secondo la modalità di arrotondamento corrente definita nell'ambiente di esecuzione matematica:

Funzione Descrizione Note
double nearbyint(double x); Restituisce il valore di x arrotondato secondo la modalità di arrotondamento corrente. Non genera eccezioni di overflow. C99
double nearbyintf(float x); Versione float di nearbyint. C99
double nearbyintl(long double x); Versione long double di nearbyint. C99
double rint(double x); Restituisce il valore di x arrotondato secondo la modalità di arrotondamento corrente. Può generare eccezioni di inexact o overflow. C99
double rintf(float x); Versione float di rint. C99
double rintl(long double x); Versione long double di rint. C99
Tabella 4: Funzioni nearbyint e rint della libreria matematica standard.

Queste funzioni arrotondano il numero passato come argomento secondo la modalità di arrotondamento corrente definita nell'ambiente di esecuzione matematica. La differenza principale tra le due è che nearbyint non genera eccezioni di overflow, mentre rint può generare eccezioni di inexact o overflow.

In pratica, a seconda della modalità di arrotondamento corrente, queste funzioni possono comportarsi come ceil, floor, trunc o round.

Funzioni di Arrotondamento a Intero

Le funzioni viste sopra arrotondano i numeri in virgola mobile ad un intero secondo regole specifiche, ma restituiscono comunque un valore in virgola mobile.

La libreria matematica standard di C fornisce anche funzioni che arrotondano i numeri in virgola mobile ad un intero e restituiscono il risultato come un valore intero.

Funzione Descrizione Note
long int lround(double x); Restituisce il valore di x arrotondato all'intero più vicino come long int. In caso di parità, arrotonda lontano da zero. C99
long int lroundf(float x); Versione float di lround. C99
long int lroundl(long double x); Versione long double di lround. C99
long int llround(double x); Restituisce il valore di x arrotondato all'intero più vicino come long long int. In caso di parità, arrotonda lontano da zero. C99
long int llroundf(float x); Versione float di llround. C99
long int llroundl(long double x); Versione long double di llround. C99
Tabella 5: Funzioni lround e llround della libreria matematica standard.

Tali funzioni si comportano in modo simile alla funzione round vista in precedenza, arrotondando il numero passato come argomento all'intero più vicino. In caso di parità, arrotondano lontano da zero.

La differenza, però, è che queste funzioni restituiscono il risultato come un valore intero (long int o long long int) invece che come un valore in virgola mobile.

Ad esempio:

  • lround(4.5) restituisce 5
  • lround(4.4) restituisce 4
  • llround(-4.5) restituisce -5
  • llround(-4.4) restituisce -4

Funzione di Resto

In linguaggio C, esiste l'operatore di modulo (o operatore di resto) % che calcola il resto della divisione tra due numeri interi.

Tuttavia, non è possibile applicare l'operatore % direttamente a numeri in virgola mobile. Per questo motivo, la libreria matematica standard di C fornisce delle funzioni specifiche per calcolare il resto della divisione tra due numeri in virgola mobile.

Funzione Descrizione Note
double fmod(double x, double y); Restituisce il resto della divisione di x per y.
double fmodf(float x, float y); Versione float di fmod. C99
double fmodl(long double x, long double y); Versione long double di fmod. C99
double remainder(double x, double y); Restituisce il resto della divisione di x per y, dove il quoziente è stato arrotondato all'intero più vicino. C99
double remainderf(float x, float y); Versione float di remainder. C99
double remainderl(long double x, long double y); Versione long double di remainder. C99
double remquo(double x, double y, int *quo); Identica a remainder ma salva anche il quoziente in *quo. C99
double remquof(float x, float y, int *quo); Versione float di remquo. C99
double remquol(long double x, long double y, int *quo); Versione long double di remquo. C99
Tabella 6: Funzioni fmod, remainder e remquo della libreria matematica standard.

Le funzioni della famiglia fmod calcolano il resto della divisione di x per y secondo la definizione matematica standard del modulo. Ad esempio, fmod(5.3, 2.0) restituisce 1.3, poiché 5.3 = 2.0 * 2 + 1.3.

Le funzioni della famiglia remainder calcolano il resto della divisione di x per y in modo particolare. Per capire come funziona, definiamo n come il quoziente di x diviso y, arrotondato al più vicino intero. La funzione remainder restituisce quindi il valore:

r = x - n \cdot y

Ad esempio, remainder(5.3, 2.0) restituisce -0.7, poiché il quoziente arrotondato è 3 (il più vicino intero a 5.3 / 2.0 = 2.65), e quindi 5.3 - 3 * 2.0 = -0.7. Se avessimo usato fmod(5.3, 2.0), avremmo ottenuto 1.3.

La funzione remquo è simile a remainder, ma in aggiunta salva il quoziente arrotondato in un intero passato come terzo argomento. Questo può essere utile se si desidera conoscere sia il resto che il quoziente della divisione.

Ad esempio:

#include <stdio.h>
#include <math.h>

int main() {
    double x = 5.3;
    double y = 2.0;
    int quo;

    double r1 = fmod(x, y);
    double r2 = remainder(x, y);
    double r3 = remquo(x, y, &quo);

    printf("fmod(%.1f, %.1f) = %.1f\n", x, y, r1);
    printf("remainder(%.1f, %.1f) = %.1f\n", x, y, r2);
    printf("remquo(%.1f, %.1f) = %.1f, quo = %d\n", x, y, r3, quo);

    return 0;
}

L'output sarà:

fmod(5.3, 2.0) = 1.3
remainder(5.3, 2.0) = -0.7
remquo(5.3, 2.0) = -0.7, quo = 3