In realtà l’errore che viene commesso non è di carattere informatico, ma puramente matematico, dimenticando l’ordine con cui vengono eseguite le operazioni matematiche.
L’ordine delle operazioni segue le regole di base: moltiplicazioni e divisioni hanno precedenza massima seguono addizioni e sottrazioni. Se si vuole cambiare l’ordine di precedenza bisogna utilizzare le parentesi. Vediamo alcuni esempi.
int valore = 1 + 2 * 3 + 4;
il risultato sarà 11.
// Prof. Michele Maffucci // Data: 08.02.2020 // Esempio 01: Ordine di esecuzione operazioni matematiche in C // per stampare una sola volta il messaggio sulla Serial Monitor bool abilitaMessaggio = 0; void setup() { // inizializzazione della comunicazione seriale Serial.begin(9600); } void loop() { // consente di visualizzare sulla Serial Monitor // una sola stampa delle stringa if (abilitaMessaggio == 0) { // ritardo che evita la doppia stampa del messaggio delay(200); Serial.println("Calcolo:"); Serial.println("valore = 1 + 2 * 3 + 4"); int valore = 1 + 2 * 3 + 4; Serial.print("valore = "); Serial.println(valore); abilitaMessaggio = 1; } }
Per rendere più evidente la sequenza di esecuzione del calcolo possiamo usare le parentesi, pertanto otterremo:
int valore = 1 + (2 * 3) + 4;
Che fornisce sempre il valore 11.
// Prof. Michele Maffucci // Data: 08.02.2020 // Esempio 02: Ordine di esecuzione operazioni matematiche in C // per stampare una sola volta il messaggio sulla Serial Monitor bool abilitaMessaggio = 0; void setup() { // inizializzazione della comunicazione seriale Serial.begin(9600); } void loop() { // consente di visualizzare sulla Serial Monitor // una sola stampa delle stringa if (abilitaMessaggio == 0) { // ritardo che evita la doppia stampa del messaggio delay(200); Serial.println("Calcolo:"); Serial.println("valore = 1 + (2 * 3) + 4"); int valore = 1 + (2 * 3) + 4; Serial.print("valore = "); Serial.println(valore); abilitaMessaggio = 1; } }
Per modificare la precedenza utilizziamo le parentesi:
int valore = ((1 + 2) * 3) + 4;
il risultato sarà 13. Viene eseguito prima il calcolo della parentesi più interna (1+2), poi si passa alla parentesi immediatamente successiva, quindi (3 * 3) e poi il risultato viene sommato a 4.
// Prof. Michele Maffucci // Data: 08.02.2020 // Esempio 03: Ordine di esecuzione operazioni matematiche in C // per stampare una sola volta il messaggio sulla Serial Monitor bool abilitaMessaggio = 0; void setup() { // inizializzazione della comunicazione seriale Serial.begin(9600); } void loop() { // consente di visualizzare sulla Serial Monitor // una sola stampa delle stringa if (abilitaMessaggio == 0) { // ritardo che evita la doppia stampa del messaggio delay(200); Serial.println("Calcolo:"); Serial.println("valore = ((1 + 2) * 3) + 4"); int valore = ((1 + 2) * 3) + 4; Serial.print("valore = "); Serial.println(valore); abilitaMessaggio = 1; } }
Ovviamente, come già spiegato precedentemente, bisognerà sempre fare attenzione che il risultato faccia parte del tipo di dati giusto, ad esempio quando effettuate una divisione tra interi il cui risultato è un numero decimale, o ancora se superate il valore massimo del tipo di dato che state utilizzando. In entrambi i casi il compilatore non vi segnalerà nessun errore.
Vediamo un esempio:
// 60 secondi in un minuto, 60 minuti in un'ora, 24 ore in un giorno long secondi_in_un_giorno = 60 * 60 * 24;
In teoria, poiché il risultato è 86.400, questo valore potrà essere contenuto in un tipo long.
Ma in realtà il valore realmente memorizzato in “secondi_in_un_giorno” è 20.864.
86.400 supera più di due volte la dimensione di un intero, il calcolo fatto dal compilatore sarà il seguente:
86.400 – 32.768 * 2 = 20.864
// Prof. Michele Maffucci // Data: 08.02.2020 // Esempio 04: Ordine di esecuzione operazioni matematiche in C // errore di calcolo dovute al tipo del dato (dimensione massima). // per stampare una sola volta il messaggio sulla Serial Monitor bool abilitaMessaggio = 0; void setup() { // inizializzazione della comunicazione seriale Serial.begin(9600); } void loop() { // consente di visualizzare sulla Serial Monitor // una sola stampa delle stringa if (abilitaMessaggio == 0) { // ritardo che evita la doppia stampa del messaggio delay(200); Serial.println("Calcolo (errato) numero di secondi in un giorno:"); Serial.println("secondi_in_un_giorno = 60 * 60 * 24"); long secondi_in_un_giorno = 60 * 60 * 24; Serial.print("Secondi in un giorno = "); Serial.println(secondi_in_un_giorno); Serial.println("Errore! Il valore doveva essere: 86.400"); Serial.println("L'errore si verifica perchè il compilatore considera i numeri di tipo int."); abilitaMessaggio = 1; } }
Ciò accade perché il compilatore C dell’IDE di Arduino vede un’espressione aritmetica composta da soli numeri interi e quindi considera il risultato come tipo int. Per evitare questo problema bisogna dire al compilatore che deve trattare l’intera espressione come un long aggiungendo L al primo valore che viene valutato nell’espressione:
long secondi_in_un_giorno = 60L * 60 * 24;
// Prof. Michele Maffucci // Data: 08.02.2020 // Esempio 05: Ordine di esecuzione operazioni matematiche in C // Uso corretto del tipo long. // per stampare una sola volta il messaggio sulla Serial Monitor bool abilitaMessaggio = 0; void setup() { // inizializzazione della comunicazione seriale Serial.begin(9600); } void loop() { // consente di visualizzare sulla Serial Monitor // una sola stampa delle stringa if (abilitaMessaggio == 0) { // ritardo che evita la doppia stampa del messaggio delay(200); Serial.println("Calcolo corretto del numero di secondi in un giorno:"); Serial.println("secondi_in_un_giorno = 60L * 60 * 24"); long secondi_in_un_giorno = 60L * 60 * 24; Serial.print("Secondi in un giorno = "); Serial.println(secondi_in_un_giorno); Serial.println("Giusto! Abbiamo detto con la L che l'intera espressione è da trattate come un long."); Serial.println(""); abilitaMessaggio = 1; } }
Attenzione sempre alle parentesi!
Se le utilizzate ad esempio come nell’esempio che segue farà andare in overflow il risultato:
long secondi_in_un_giorno_piu_uno = 1L + 60 * (60 * 24);
// Prof. Michele Maffucci // Data: 08.02.2020 // Esempio 06: Ordine di esecuzione operazioni matematiche in C // L'uso non corretto delle parentesi fa andare in overflow il risultato. // per stampare una sola volta il messaggio sulla Serial Monitor bool abilitaMessaggio = 0; void setup() { // inizializzazione della comunicazione seriale Serial.begin(9600); } void loop() { // consente di visualizzare sulla Serial Monitor // una sola stampa delle stringa if (abilitaMessaggio == 0) { // ritardo che evita la doppia stampa del messaggio delay(200); Serial.println("Calcolo errato somma 1 al numero di secondi in un giorno"); Serial.println("secondi_in_un_giorno_piu_uno = 1L + 60 * (60 * 24)"); long secondi_in_un_giorno_piu_uno = 1L + 60 * (60 * 24); Serial.print("Secondi in un giorno + 1 = "); Serial.println(secondi_in_un_giorno_piu_uno); Serial.println("Sbagliato! Attenzione sempre alle parentesi!"); Serial.println("Se le utilizzate ad esempio come indicato farà andare in overflow il risultato"); abilitaMessaggio = 1; } }
mentre la seguente espressione non farà andare in overflow il calcolo:
long secondi_in_un_giorno_piu_uno = 1 + 60 * (60L * 24);
// Prof. Michele Maffucci // Data: 08.02.2020 // Esempio 07: Ordine di esecuzione operazioni matematiche in C // L'ordine del calcolo viene stabilito dalle parentesi, in questo modo // il calcolo non farà andare in overflow il risultato. // per stampare una sola volta il messaggio sulla Serial Monitor bool abilitaMessaggio = 0; void setup() { // inizializzazione della comunicazione seriale Serial.begin(9600); } void loop() { // consente di visualizzare sulla Serial Monitor // una sola stampa delle stringa if (abilitaMessaggio == 0) { // ritardo che evita la doppia stampa del messaggio delay(200); Serial.println("Calcolo errato somma 1 al numero di secondi in un giorno"); Serial.println("secondi_in_un_giorno_piu_uno = 1 + 60 * (60L * 24)"); long secondi_in_un_giorno_piu_uno = 1 + 60 * (60L * 24); Serial.print("Secondi in un giorno + 1 = "); Serial.println(secondi_in_un_giorno_piu_uno); Serial.println("Corretto! Il calcolo inizierà dalle parentesi tonde."); Serial.println("E' stata aggiunta la L al primo operando tra le parentesi tonde."); abilitaMessaggio = 1; } }
Buon Coding a tutti 🙂