Archivi tag: calcolo

Errori comuni nell’uso di Arduino – ordine esecuzione operazioni matematiche e overflow nei calcoli


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 🙂

Corso di elettrotecnica ed elettronica: richiami di Matematica – regola del triangolo – Lezione 7

banner-corso-elettrotecnica-elettronica

Mi capita sovente, soprattutto con gli studenti del primo anno delle superiori di rilevare la difficoltà di ricavare la formula inversa da una data costituita da tre termini.

La regola del triangolo è un metodo pratico per ricavare la formula inversa da una formula matematica formata da tre termini. A titolo di esempio proviamo ad usare questa regola per trovare la resistenza elettrica R dalla nota legge di Ohm (che vedremo più avanti):

[pmath size=16]V = R*I[/pmath]

dove V rappresenta la tensione elettrica espressa in Volt ed I la corrente elettrica espressa in Ampere.

Da questa si ricava la formula inversa:

[pmath size=16]R=V/I[/pmath]

ma anche:

[pmath size=16]I=V/R[/pmath]

Per ricavare le formule inverse usando il metodo del triangolo si parte dalla formula:

[pmath size=16]V = R*I[/pmath]

Si pone al vertice il termine di sinistra, nel nostro caso [pmath size=12]V[/pmath] e al di sotto della linea di separazione i termini che sono moltiplicati tra loro, nel nostro caso [pmath size=12]R*I[/pmath], il disegno del triangolo risulta il seguente:

triangolo

dove la linea orizzontale indica la linea di frazione.

Per ricavare la grandezza incognita, ad esempio la [pmath size=16]R[/pmath] è sufficiente coprirla con il dito e leggere ciò che è rimasto scoperto, cioè:

[pmath size=16]V/I[/pmath]

vi

Nel caso in cui l’incognita fosse la I allora si avrebbe:

[pmath size=16]I=V/R[/pmath]

 vr

 

Allo stesso modo nel caso in cui fosse da ricavare la [pmath size=16]V[/pmath] è sufficiente coprirla nel triangolo:

[pmath size=16]R*I[/pmath]

ri

Vedremo più avanti come utilizzare questa semplicissima regoletta con altre grandezze.