Assegnamento e Operazioni Aritmetiche in Fortran
In questa lezione esploriamo il funzionamento dei calcoli aritmetici in Fortran, soffermandoci sulle istruzioni di assegnazione, l'ordine di valutazione degli operatori (precedenza delle operazioni), l'aritmetica mista tra interi e numeri in virgola mobile e le particolarità dell'esponenziazione.
Impareremo come Fortran gestisce espressioni con differenti tipi di dato, quali insidie evitare (in particolare con la divisione intera e la limitata precisione dei real
) e in che modo utilizzare le funzioni di conversione per controllare esplicitamente il tipo dei risultati.
Assegnamento e Calcoli Aritmetici in Fortran
Le operazioni di calcolo numerico in Fortran vengono espresse tramite istruzioni di assegnazione (assignment statement), la cui forma generale è:
nome_variabile = espressione
L'istruzione di assegnazione calcola il valore dell'espressione a destra del segno di uguale e assegna tale valore alla variabile a sinistra.
Attenzione a non confondere l'uguale con il segno di equivalenza in algebra. In Fortran, i = i + 1
significa: Prendi il valore corrente di i
, aggiungigli 1, e memorizza il risultato in i
.
L'espressione a destra dell'uguale può combinare costanti, variabili, parentesi e operatori aritmetici o logici. Gli operatori aritmetici standard in Fortran sono:
+
(Addizione)-
(Sottrazione)*
(Moltiplicazione)/
(Divisione)**
(Esponenziazione)
I simboli usati in Fortran per moltiplicazione, divisione ed esponenziazione sono diversi dai simboli abituali in matematica (×, ÷, ^), perché originariamente (negli anni '50) si doveva tener conto dei caratteri disponibili sulle tastiere dei computer.
I cinque operatori di cui sopra sono binari: appaiono fra due variabili o costanti, ad esempio:
a + b
a - b
a ** b
a * b
a / b
Inoltre, i simboli +
e -
possono comportarsi da operatori unari, applicandosi a un'unica variabile o costante, come in:
+23
-a
Regole per l'Uso degli Operatori Aritmetici in Fortran
Esistono delle regole ben precise per l'uso degli operatori aritmetici in Fortran:
-
Non possono comparire due operatori di seguito.
Perciò l'espressione
a * -b
è illegale; si deve scriverea * (-b)
. Analogamente,a ** -2
va scrittoa ** (-2)
. -
La moltiplicazione implicita è illegale.
Un'espressione come
x(y + z)
non è valida; bisogna scriverla comex * (y + z)
. Questa regola è stata introdotta per evitare ambiguità. -
Le parentesi si possono usare per raggruppare i termini come si desidera.
Le espressioni dentro le parentesi vengono valutate prima di quelle esterne. Ad esempio:
2 ** ((8+2)/5) = 2 ** (10/5) = 2 ** 2 = 4
Aritmetica con i Numeri Interi
L'aritmetica intera (integer arithmetic) opera soltanto con numeri interi (costanti o variabili). Il risultato è sempre un intero. È particolarmente importante ricordare che, nel caso di divisione tra interi, non c'è parte frazionaria nel risultato: il calcolatore tronca la parte decimale.
Ciò può produrre risultati inattesi, come:
A causa di questo comportamento, è sconsigliabile usare interi per rappresentare grandezze del mondo reale (come distanza, velocità o tempo) che possono variare in modo continuo. Gli interi sono indicati solo per entità intrinsecamente intere, come contatori o indici di array.
Nelle divisioni tra interi, il risultato è troncato.
Attenzione all'aritmetica intera. La divisione tra interi spesso produce risultati inaspettati dal momento che la parte frazionaria viene troncata.
Aritmetica con i Numeri Reali
L'aritmetica reale (real arithmetic o floating-point arithmetic) coinvolge costanti e variabili in virgola mobile (real). Il risultato di un'operazione tra real è sempre un numero reale, in linea con le aspettative. Ad esempio:
(approssimato)
Tuttavia, i numeri reali presentano peculiarità dovute alla precisione finita. Alcuni valori (come 1/3) non possono essere rappresentati esattamente, e ciò provoca possibili differenze tra il valore teorico e la sua rappresentazione in memoria.
Per esempio, su alcune macchine:
ma
Quando si lavora con i real
, bisogna essere prudenti nei confronti di uguaglianza, poiché due espressioni teoricamente uguali possono dare risultati leggermente diversi.
I numeri reali possono presentare errori di approssimazione.
Bisogna prestare attenzione all'aritmetica con i real
: a causa della precisione limitata, espressioni teoricamente identiche possono produrre risultati leggermente differenti.
Precedenza degli Operatori
Spesso, molte operazioni aritmetiche vengono combinate in un'unica espressione.
Ad esempio, si consideri l'equazione per la distanza percorsa da un oggetto che parte da fermo e subisce un'accelerazione costante:
distance = 0.5 * accel * time ** 2
In questa espressione compaiono due moltiplicazioni e un'esponenziazione.
L'ordine di valutazione è fondamentale, perché se l'esponenziazione avviene prima della moltiplicazione, l'equazione è equivalente a:
distance = 0.5 * accel * (time ** 2)
Ma se invece la moltiplicazione viene valutata prima dell'esponenziazione, otteniamo:
distance = (0.5 * accel * time) ** 2
Queste due equazioni producono risultati diversi, quindi dobbiamo stabilire in modo univoco quale operazione viene eseguita prima.
Per rendere univoca la valutazione delle espressioni, Fortran adotta una serie di regole (o precedenza degli operatori) che definiscono l'ordine di valutazione. In generale, queste regole seguono la normale algebra, e sono:
- Le espressioni tra parentesi sono valutate per prime, dall'interno verso l'esterno.
- Le esponenziazioni sono valutate in seguito, procedendo da destra a sinistra.
- Le moltiplicazioni e divisioni sono valutate dopo, procedendo da sinistra a destra.
- Le addizioni e sottrazioni vengono infine valutate, da sinistra a destra.
Applicando queste regole, vediamo che il primo dei due possibili ordini (time
al quadrato prima della moltiplicazione) è quello corretto.
Esiste un'acronimo usato da alcune persone per ricordare l'ordine di valutazione: PEMDAS, dove le iniziali stanno per Parentesi, Esponenti, Moltiplicazioni, Divisioni, Addizioni, Sottrazioni.
Esempi
Le variabili a
, b
, c
, d
, e
, f
, g
hanno i seguenti valori iniziali:
a = 3.
b = 2.
c = 5.
d = 4.
e = 10.
f = 2.
g = 3.
Valutiamo le seguenti istruzioni di assegnazione in Fortran:
- (a)
output = a*b+c*d+e/f**g
- (b)
output = a*(b+c)*d+(e/f)**g
- (c)
output = a*(b+c)*(d+e)/f**g
Soluzione per (a):
output = a*b+c*d+e/f**g
- Riempire con i numeri:
output = 3.*2.+5.*4.+10./2.**3.
- Prima valutare
2.**3.
: - Ora valutare moltiplicazioni e divisioni da sinistra a destra:
3.*2. = 6
5.*4. = 20
10./8. = 1.25
- Infine sommare i risultati:
6 + 20 + 1.25 = 27.25
Soluzione per (b):
output = a*(b+c)*d+(e/f)**g
- Riempire con i numeri:
output = 3.*(2.+5.)*4.+(10./2.)**3.
- Prima valutare le parentesi:
2.+5. = 7.
- Quindi
3.*7.*4. = 3.*7. = 21., 21.*4. = 84.
- Quindi
- Valutare
(10./2.)**3.
:10./2. = 5.
, poi
- Sommare i risultati:
84 + 125 = 209
Soluzione per (c):
output = a*(b+c)*(d+e)/f**g
- Riempire con i numeri:
output = 3.*(2.+5.)*(4.+10.)/2.**3.
- Prima valutare le parentesi:
2.+5. = 7.
e4.+10. = 14.
- Ora valutare l'esponenziazione:
2.**3. = 8
- Eseguire le moltiplicazioni e divisioni da sinistra a destra:
3.*7. = 21.
21.*14. = 294.
294./8. = 36.75
Il risultato finale di (a) è 27.25, di (b) è 209, e di (c) è 36.75. Si nota come l'ordine delle operazioni influenzi in modo significativo il risultato di un'espressione algebrica.
Vediamo altri esempi.
Le variabili a
, b
, c
hanno i seguenti valori:
a = 3.
b = 2.
c = 3.
Valutiamo le istruzioni di assegnazione Fortran:
- (a)
output = a**(b**c)
- (b)
output = (a**b)**c
- (c)
output = a**b**c
Soluzione per (a):
output = a**(b**c)
- Riempire con i numeri:
output = 3.**(2.**3.)
- Prima valutare l'espressione nelle parentesi:
2.**3. = 8
- Poi valutare l'espressione rimanente:
3.**8 = 6561
Soluzione per (b):
output = (a**b)**c
- Riempire con i numeri:
output = (3.**2.)**3.
- Prima valutare l'espressione tra parentesi:
3.**2. = 9
- Poi l'espressione rimanente:
9**3 = 729
Soluzione per (c):
output = a**b**c
- Riempire con i numeri:
output = 3.**2.**3.
- Prima valutare l'esponente più a destra:
2.**3 = 8
- Ora valutare l'esponente rimanente:
3.**8 = 6561
I risultati di (a) e (c) sono entrambi 6561, mentre (b) dà 729. È evidente che scrivere a**(b**c)
o (a**b)**c
produce esiti diversi (eccetto che per valori specifici di a, b, c), e che a**b**c
è meno chiaro se non si conosce l'ordine di valutazione predefinito (da destra a sinistra per l'esponenziazione).
Nel dubbio, usare le parentesi.
Si consiglia di usare le parentesi quando necessario, per rendere le equazioni il più chiare e semplici possibile.
Se si usano le parentesi, assicurarsi che siano bilanciate (lo stesso numero di parentesi aperte e chiuse). Errori come:
(2. + 4.) / 2.)
causano un errore di compilazione (parentesi non corrispondenti).
Aritmetica Mista (Mixed-Mode Arithmetic)
Quando un'operazione aritmetica coinvolge due numeri reali, il risultato immediato è di tipo real
. Analogamente, quando l'operazione coinvolge due interi, il risultato è di tipo integer
. In generale, le operazioni aritmetiche sono definite tra numeri dello stesso tipo (es. addizione di due real o di due integer). L'addizione di un real e di un integer è un'operazione in Fortran sì, sebbene i due tipi (real
e integer
) siano memorizzati in forma completamente diversa nel computer.
Che accade se l'operazione è tra un real e un integer? Un'espressione che contiene sia numeri real sia interi è chiamata mixed-mode expression, e l'aritmetica che coinvolge real e integer si definisce mixed-mode arithmetic.
In caso di operazione tra un numero real
e uno integer
, Fortran converte l'intero in un numero reale, poi esegue l'operazione, ottenendo un risultato di tipo real
. Ad esempio:
- Integer expression:
3 / 2
produce 1 (risultato intero). - Real expression:
3. / 2.
produce 1.5 (risultato reale). - Mixed-mode expression:
3. / 2
produce 1.5 (risultato reale).
Le regole che governano la mixed-mode arithmetic possono creare confusione, soprattutto quando c'è di mezzo una divisione. Consideriamo le seguenti espressioni:
Espressione | Risultato |
---|---|
1 + 1/4 | 1 |
1. + 1/4 | 1. |
1 + 1./4 | 1.25 |
- Nell'espressione 1, compaiono solo interi (
1
,1
e4
), quindi viene usata l'aritmetica intera.1 / 4 = 0
e dunque il risultato finale è 1 (uninteger
). - L'espressione 2 è mixed-mode (contiene
real
einteger
), ma l'ordine di esecuzione è importante: la divisione (tra interi) avviene prima dell'addizione, per la precedenza delle operazioni. Quindi1 / 4 = 0
in aritmetica intera. Poi1.
+0
converte0
inreal
e fornisce1.
(reale). - L'espressione 3 è anch'essa mixed-mode. La prima operazione da eseguire è la divisione tra
1.
(unreal
) e4
(uninteger
). Per eseguirla, Fortran converte4
in4.
e fa1. / 4. = 0.25
. La successiva addizione è1 + 0.25
, in cui1
viene convertito in real, ottenendo 1.25 (un valore reale).
In sintesi:
- Un'operazione tra un
integer
e unreal
si chiama mixed-mode operation. Un'espressione che contenga uno o più di questi casi è detta mixed-mode expression. - In presenza di un'operazione mista, Fortran converte l'
integer
inreal
, poi esegue l'operazione in aritmetica reale. - La conversione automatica di tipo non avviene finché non ci sono contemporaneamente un real e un integer nella stessa operazione. È quindi possibile che una parte di espressione venga valutata in aritmetica intera, e poi un'altra parte in aritmetica reale.
La conversione di tipo avviene anche quando la variabile a cui l'espressione è assegnata è di tipo diverso dal risultato dell'espressione. Ad esempio:
nres = 1.25 + 9 / 4
Se nres
è un integer
, l'espressione a destra si valuta a 3.25 (un real
). Poiché nres
è intero, 3.25 viene convertito in 3 prima di essere memorizzato.
Evitare le Espressioni Mixed-Mode.
Le espressioni mixed-mode sono pericolose, perché difficili da capire e possono produrre risultati fuorvianti. Evitarle quando possibile.
Funzioni di Conversione del Tipo
Fortran include cinque funzioni di conversione per gestire in modo esplicito la conversione tra integer e real:
Nome | Tipo arg. | Tipo risultato | Commenti |
---|---|---|---|
INT(x) |
REAL |
INTEGER |
Parte intera di |
NINT(x) |
REAL |
INTEGER |
Intero più vicino a |
CEILING(x) |
REAL |
INTEGER |
Intero più vicino |
FLOOR(x) |
REAL |
INTEGER |
Intero più vicino |
REAL(i) |
INTEGER |
REAL |
Converte i (intero) in real |
Tramite REAL
, INT
, NINT
, CEILING
, FLOOR
, si possono evitare espressioni miste indesiderate.
REAL
converte unINTEGER
in unREAL
.INT
,CEILING
,NINT
,FLOOR
convertonoREAL
inINTEGER
con diverse modalità (troncamento, arrotondamento, ecc.).
Per capire la differenza tra queste funzioni, consideriamo i numeri REAL
: 2.9995
e -2.9995
. I risultati delle funzioni con questi input sono:
Funzione | Risultato | Descrizione |
---|---|---|
INT(2.9995) |
Tronca |
|
NINT(2.9995) |
Arrotonda |
|
CEILING(2.9995) |
Seleziona l'intero |
|
FLOOR(2.9995) |
Seleziona l'intero |
|
INT(-2.9995) |
Tronca |
|
NINT(-2.9995) |
Arrotonda |
|
CEILING(-2.9995) |
Seleziona l'intero |
|
FLOOR(-2.9995) |
Seleziona l'intero |
NINT
è particolarmente utile quando convertiamo un real in integer e non vogliamo che piccoli errori di arrotondamento influiscano sul risultato.
Aritmetica Mista ed Esponenziazione
Come regola generale, le operazioni aritmetiche mixed-mode non sono desiderabili, perché risultano difficili da comprendere e talvolta possono produrre risultati inaspettati.
Tuttavia, esiste un'eccezione: l'esponenziazione.
Per l'esponenziazione, l'aritmetica mista può essere effettivamente utile.
Per capire perché, consideriamo l'istruzione di assegnazione:
REAL :: y = 2.0
INTEGER :: n = 3
REAL :: result
result = y ** n
dove result
e y
sono real
, e n
è un intero (real
). L'espressione y ** n
significa moltiplica y
per se stesso n
volte; se n
è un intero, questa operazione è semplice.
Dato che y
è real
, il calcolatore sta in realtà facendo aritmetica reale e non mixed-mode.
Ora consideriamo la dichiarazione di assegnazione:
REAL :: y = 2.0
REAL :: x = 2.5
REAL :: result
result = y ** x
dove result
, y
e x
sono real
. L'espressione y ** x
indica usa y
come fattore x
volte; ma questa volta x
potrebbe non essere un intero, per esempio 2.5.
Non è fisicamente possibile moltiplicare y
per se stesso 2.5 volte in modo diretto, quindi si ricorre a metodi indiretti, tipicamente la formula algebrica standard:
Usando questa equazione, possiamo calcolare
Questa tecnica funziona, ma è più lenta e meno accurata di una semplice serie di moltiplicazioni. Pertanto, se si può scegliere, conviene usare un esponente intero piuttosto che un esponente reale.
Usare Esponenti Interi quando Possibile.
Usa esponenti interi anziché reali quando possibile in espressioni di esponenziazione. Questo evita l'aritmetica mista e rende i calcoli più rapidi e precisi.
Numeri negativi e potenze reali
Bisogna notare anche che non è possibile elevare un numero negativo a una potenza reale negativa. Elevare un numero negativo a una potenza intera, invece, è perfettamente legale. Ad esempio:
Tuttavia, elevare un numero negativo a una potenza reale produce un errore a runtime, perché il logaritmo naturale di un numero negativo non è definito. Di conseguenza, l'espressione:
(-2.0) ** 2.0
non funziona.
Evitare Potenze Reali di Numeri Negativi.
In Fortran (e in matematica in generale), non è possibile elevare un numero negativo a una potenza reale. Questo genera un errore a runtime.
In Sintesi
I Concetti Chiave da Ricordare di questa lezione sono:
-
Assegnazioni e Operatori Aritmetici
In Fortran l'uguaglianza
=
è un operatore di assegnazione: calcola il valore dell'espressione a destra e lo memorizza nella variabile a sinistra. Gli operatori standard (+
,-
,*
,/
,**
) seguono regole precise, tra cui il divieto di “moltiplicazione implicita” e la necessità di racchiudere tra parentesi gli argomenti con segno negativo (ad esempio,(-b)
). -
Aritmetica Intera e Reale
- L'aritmetica
integer
tronca i risultati (ad esempio,3/4 = 0
), generando possibili comportamenti inaspettati se si rappresentano grandezze continue. - L'aritmetica
real
(floating-point) soffre di precisione finita: valori come 1/3 non possono essere memorizzati esattamente, quindi due espressioni teoricamente uguali potrebbero produrre risultati lievemente diversi.
- L'aritmetica
-
Precedenza delle Operazioni
Fortran segue l'ordine: parentesi, esponenziazione, moltiplicazione/divisione (da sinistra a destra), addizione/sottrazione (da sinistra a destra). L'uso accorto delle parentesi rende il codice più leggibile e impedisce ambiguità.
-
Aritmetica Mista (Mixed-Mode)
Quando un'espressione contiene insieme interi e reali, Fortran converte l'intero in
real
prima di eseguire l'operazione. Questo può portare a risultati diversi a seconda dell'ordine di valutazione: un pezzo di espressione potrebbe essere intero e un altro reale. Per evitare malintesi, è bene ricorrere alle funzioni di conversione (REAL
,INT
,NINT
,CEILING
,FLOOR
) o dichiarare i tipi in modo esplicito. -
Esponenziazione
L'espressione
y ** n
(doven
è un intero) è semplice e viene eseguita come una serie di moltiplicazioni. Invece,y ** x
(conx
reale) richiede calcoli indiretti (ad esempio), più lenti e meno accurati. Non è consentito elevare a potenze reali un numero negativo, poiché il logaritmo di un numero negativo non è definito.