Tipi Floating Point in Object Pascal

Concetti Chiave
  • In Object Pascal, i tipi di dati floating point sono utilizzati per rappresentare numeri reali con precisione variabile.
  • I principali tipi di dati floating point in Object Pascal sono Single, Double e Extended, ciascuno con diverse dimensioni e precisioni.
  • I tipi Comp e Currency sono tipi numerici non-ordinali che offrono rappresentazioni precise per numeri grandi e valori monetari, rispettivamente.
  • I tipi di dati floating point non supportano operazioni ordinali come Inc, Dec, High, Low e Ord a causa della loro natura approssimativa.
  • I numeri floating point possono introdurre imprecisioni nei calcoli a causa della loro rappresentazione binaria, quindi è importante essere consapevoli di questo quando si lavora con questi tipi di dati.

Tipi Floating Point

I tipi di dato floating point sono usati per rappresentare numeri reali, cioè numeri che possono avere una parte frazionaria. Questi tipi di dati sono essenziali quando si lavora con valori che richiedono una precisione decimale, come misurazioni scientifiche, calcoli finanziari e grafica computerizzata.

A differenza dei numeri interi di vari tipi, i tipi floating point non possiedono il concetto di successione. In altre parole, sebbene possano essere rappresentati con un insieme ordinale di valori, i numeri floating point non sono ordinali (hanno il concetto di ordine, ma non il concetto di una sequenza di elementi) e sono rappresentati da qualche valore approssimativo, con qualche errore nella loro rappresentazione.

I numeri floating-point esistono in vari formati, a seconda del numero di byte utilizzati per rappresentarli e della qualità dell'approssimazione. Ecco una lista dei tipi di dati floating-point in Object Pascal:

Tipo Descrizione
Single Tipo Floating Point a singola precisione implementato con 4 byte.
Double Tipo Floating Point a doppia precisione implementato con 8 byte.
Extended Tipo Floating Point a precisione estesa implementato con 10 byte nel compilatore Delphi Win32 originale.
Tabella 1: Tipi Floating Point in Object Pascal

Questi sono tutti tipi di dati floating-point con diversa precisione messi a disposizione da Object Pascal. Bisogna, però fare alcune osservazioni:

  • I tipi Single e Double sono i tipi di dati floating-point più comunemente usati. La maggior parte delle operazioni matematiche in Object Pascal utilizza questi tipi. Inoltre essi corrispondono ai tipi di dati float e double del linguaggio C e allo standard IEEE per la rappresentazione dei numeri in virgola mobile, il che li rende ideali per l'interoperabilità con librerie esterne scritte in C o altri linguaggi.
  • In particolare, il tipo Double ha un alias, Real (che è il tipo di dati floating-point predefinito in Object Pascal). Quindi, quando si dichiara una variabile di tipo Real, in realtà si sta dichiarando una variabile di tipo Double.
  • Il tipo Extended offre una precisione maggiore rispetto a Double, ma non è sempre supportato su tutte le piattaforme e, inoltre, non fa parte dello standard IEEE. Per questo motivo, il suo uso è meno comune e si dovrebbe considerare attentamente se è necessario utilizzarlo. Infatti, mentre Single e Double sono ampiamente supportati dai processori moderni, Extended potrebbe non esserlo e quindi il suo utilizzo potrebbe comportare un degrado delle prestazioni.

Ci sono anche due particolari tipi di dati numerici non-ordinali che si possono utilizzare per rappresentare numeri con una rappresentazione precisa, non approssimativa:

Tipo Descrizione
Comp Descrive interi molto grandi utilizzando 8 byte o 64 bit (che possono contenere numeri con 18 cifre decimali). L'idea è di rappresentare numeri grandi senza perdita di precisione, a differenza dei corrispondenti valori floating point.
Currency Indica un valore decimale a virgola fissa con quattro cifre decimali, e la stessa rappresentazione a 64 bit del tipo Comp. Come il nome implica, il tipo di dati Currency è stato aggiunto per gestire valori monetari molto precisi, con quattro posizioni decimali (di nuovo senza perdita di precisione nei calcoli).
Tabella 2: Tipi numerici Non-Ordinali in Object Pascal

Tutti questi tipi di dati non-ordinali non hanno i concetti delle funzioni High, Low o Ord. I tipi Real rappresentano (in teoria) un insieme infinito di numeri; i tipi ordinali rappresentano un insieme fisso di valori.

Concetti Chiave sui Numeri Floating Point

Prima abbiamo detto che i tipi di dati floating point non sono ordinali. Cosa vuol dire esattamente?

Se consideriamo i numeri interi, possiamo vedere che sebbene infiniti, costituiscono comunque un insieme discreto di numeri. Tra due numeri interi qualsiasi, c'è sempre un numero finito di numeri interi. Ad esempio, tra 32 e 34 c'è solo il numero 33. Tra 1 e 4 ci sono i numeri 2 e 3. Tra 23 e 24 non ce n'è nessuno. Questo significa che i numeri interi hanno un ordine ben definito e una sequenza. Possiamo sempre dire quale numero viene dopo un altro numero, e possiamo contare quanti numeri ci sono tra due numeri interi.

Nel caso dei linguaggi di programmazione, quando abbiamo un Integer, possiamo usare la funzione Inc per ottenere il numero successivo, e la funzione Dec per ottenere il numero precedente. Inoltre, poiché non possiamo rappresentare tutti i numeri interi, dal momento che sono infiniti, ma solo un sottoinsieme finito di essi, possiamo usare le funzioni High e Low per ottenere il valore più alto e più basso che possiamo rappresentare con quel tipo di dati.

La situazione cambia, invece, con i numeri reali. Tra due numeri reali qualsiasi, per quanto vicini possano essere, c'è sempre un numero infinito di numeri reali. Ad esempio, tra 1.1 e 1.2 ci sono infiniti numeri reali: 1.11, 1.111, 1.1111, e così via. Si dice, appunto, che l'insieme dei numeri reali è un insieme denso. Questo significa che non possiamo dire quale numero viene dopo un altro numero reale, perché tra due numeri reali qualsiasi c'è sempre un altro numero reale. Inoltre, non possiamo contare quanti numeri reali ci sono tra due numeri reali, perché ce ne sono infiniti.

I numeri floating point, come Single e Double, sono un'approssimazione dei numeri reali. Non possiamo rappresentare qualunque numero reale con essi e, comunque, non ha senso parlare di numero successivo o precedente di un altro. Motivo per cui, i tipi floating point non supportano le operazioni ordinali che i tipi interi, invece, supportano.

Per questa ragione, mentre ha senso chiedere la posizione ordinale del carattere 'w' nell'intervallo del tipo di dati Char, non ha senso affatto fare la stessa domanda su 7143.1562 nell'intervallo di un tipo di dati floating-point. Anche se comunque si può infatti sapere se un numero reale ha un valore più alto di un altro, non ha senso chiedere quanti numeri reali esistono prima di un dato numero (questo è il significato della funzione Ord).

Un altro concetto chiave dietro i valori floating point è che la loro implementazione non può rappresentare tutti i numeri precisamente. È spesso il caso che il risultato di un calcolo che ci si aspetta essere un numero specifico (a volte un intero), potrebbe in realtà essere un valore approssimativo di esso.

Consideriamo, ad esempio, questo programma

program TestFloatingPoint;

var
    s: Single;
begin
    s := 0.5 * 0.2;
    Writeln(s);
end.

Ci si aspetterebbe che il risultato sia 0.1, mentre in realtà il risultato ottenuto sarà:

 1.000000015E-01

Questo è vicino al valore atteso, ma non esattamente quello. La motivazione di questo comportamento è piuttosto tecnica, ma in breve, i numeri floating point sono rappresentati in un formato binario che non può rappresentare esattamente tutti i numeri decimali. Alcuni numeri decimali, come 0.1, non possono essere rappresentati esattamente in binario, il che porta a piccole imprecisioni nei calcoli.

Funzioni Floating Point in Free Pascal

Il compilatore Free Pascal include una serie di funzioni matematiche che operano sui tipi di dati floating point. Queste funzioni sono definite nell'unità Math, che deve essere inclusa nella clausola uses del programma.

Alcune delle funzioni più comuni sono:

Funzione Descrizione
Abs(x) Restituisce il valore assoluto di x.
Sqr(x) Restituisce il quadrato di x.
Sqrt(x) Restituisce la radice quadrata di x.
Sin(x) Restituisce il seno di x (in radianti).
Cos(x) Restituisce il coseno di x (in radianti).
Tan(x) Restituisce la tangente di x (in radianti).
ArcSin(x) Restituisce l'arcoseno di x (in radianti).
ArcCos(x) Restituisce l'arcocoseno di x (in radianti).
ArcTan(x) Restituisce l'arcotangente di x (in radianti).
Ln(x) Restituisce il logaritmo naturale di x.
Log10(x) Restituisce il logaritmo in base 10 di x.
Exp(x) Restituisce e elevato alla potenza di x.
Power(x, y) Restituisce x elevato alla potenza di y.
Tabella 3: Funzioni Matematiche in Free Pascal

Funzioni Helper dei Tipi Floating Point in Delphi

Delphi fornisce anche una serie di funzioni helper per i tipi di dati floating point, che permettono di eseguire operazioni direttamente sulle variabili di questi tipi, come se fossero oggetti. Queste funzioni sono accessibili tramite il punto (.) dopo il nome della variabile.

Ecco alcune delle funzioni helper più comuni per i tipi di dati floating point:

Funzione Descrizione
Sign Restituisce -1 se il numero è negativo, 0 se è zero, e 1 se è positivo.
Exponent Restituisce l'esponente del numero in notazione scientifica.
Fraction Restituisce la parte frazionaria del numero.
Mantissa Restituisce la mantissa del numero in notazione scientifica.
SpecialType Restituisce valori speciali floating point (NaN, infinito, ecc.).
BuildUp Costruisce un numero floating point da segno, esponente e mantissa.
ToString Converte il numero in una stringa.
Tabella 4: Funzioni Helper dei Tipi Floating Point in Delphi

Inoltre, Delphi include alcune funzioni globali che operano sui tipi di dati floating point. Tali funzioni sono definite nell'unità System.Math e includono:

Funzione Descrizione
IsNan(x) Restituisce True se x è "Not a Number" (NaN).
IsInfinite(x) Restituisce True se x è infinito.
IsFinite(x) Restituisce True se x è un numero finito.
IsZero(x) Restituisce True se x è zero.
Round(x) Arrotonda x al numero intero più vicino.
Trunc(x) Restituisce la parte intera di x, troncando la parte frazionaria.
Ceil(x) Restituisce il più piccolo intero maggiore o uguale a x.
Floor(x) Restituisce il più grande intero minore o uguale a x.
Frac(x) Restituisce la parte frazionaria di x.
Int(x) Restituisce la parte intera di x, arrotondando verso meno infinito.
Pi Restituisce il valore di π (pi greco).
E Restituisce il valore di e (base dei logaritmi naturali).
Sqrt(x) Restituisce la radice quadrata di x.
Power(x, y) Restituisce x elevato alla potenza di y.
LogN(base, x) Restituisce il logaritmo di x in base base.
Log2(x) Restituisce il logaritmo in base 2 di x.
Log10(x) Restituisce il logaritmo in base 10 di x.
Exp(x) Restituisce e elevato alla potenza di x.
Sin(x) Restituisce il seno di x (in radianti).
Cos(x) Restituisce il coseno di x (in radianti).
Tan(x) Restituisce la tangente di x (in radianti).
ArcSin(x) Restituisce l'arcoseno di x (in radianti).
ArcCos(x) Restituisce l'arcocoseno di x (in radianti).
ArcTan(x) Restituisce l'arcotangente di x (in radianti).
ArcTan2(y, x) Restituisce l'arcotangente di y/x considerando il quadrante.
Tabella 5: Funzioni definite in System.Math per i Tipi Floating Point in Delphi