Ieri pomeriggio ho terminato il mio piccolissimo progettino di irrigazione automatica.
L'ho provato più volte e andava bene.... così ho settato il timer alle 3 di notte per 1minuto... e stamattina ho trovato un "lago".
Ho subito individuato il problema e risolto: overflow nella variabile tempo
Però volevo dei chiarimenti perché non mi è chiaro il problema.
Il codice ERA:
short tempo = 60; //in secondi
digitalWrite(4, LOW);
delay(tempo * 1000); // TEMPO DI IRRIGAZIONE
digitalWrite(4, HIGH);
e ho corretto in (poi domani mattina vi dico!):
unsigned long tempo = 60; //in secondi
digitalWrite(4, LOW);
delay(tempo * 1000); // TEMPO DI IRRIGAZIONE
digitalWrite(4, HIGH);
Non riesco a capire perché ho dovuto dichiarare la variabile tempo da short ad unsigned long se alla funzione delay passo un operazione.
Non dovrebbe farsi carico delay di prendere il risultato dell'operazione di moltiplicazione?
tradotto in pseudo codice, capisco questo:
unsigned long tempo = 60;
tempo = tempo * 1000;
delay (tempo);
Semplice è andato in overflow numerico, short supporta fino a 32767 tu chiedi di fare 601000 al compilatore, lui sa che il 60 (tempo) è uno short, mille anche ma la loro moltiplicazione invece non ci sta.Credo che se tu fai tempo1000UL forzi comunque il tipo corretto senza il difetto riscontrato e senza cambiare il tipo alla variabile
eh si, questo lo avevo capito per questo poi ho corretto in unsigned long.
la mia curiosità era sul "risultato" dell'operazione che è passato come parametro a delay.... pensavo (speravo) che fosse demandato a delay il cast della variabile.
il cast del parametro avverrà sicuramente visto che il delay accetta come parametro un unsigned long ma le operazioni vengono svolte dal compilatore e dalla MCU in ordine differente, ovvero in fase di compilazione il compilatore decide che tipo è necessario a quell'operazione di moltiplicazione, e questo poi viene sfruttato per passarlo come parametro alla funzione delay, visto che un short è contenuto in un unsigned long non è detto che ti segnali alcun warning di compilazione ma in fase runtime succede il patatrac
fabpolli:
lui sa che il 60 (tempo) è uno short, mille anche la sua moltiplicazione invece non ci sta.
"(mille?) anche la sua moltiplicazione invece non ci sta." Uhmm...
"mille anche la sua moltiplicazione, invece non ci sta." Uhmm...
"mille anche. La sua moltiplicazione, invece, non ci sta." Mi sa che è l'interpretazione giusta, ma l'ho capito solo adesso che l'ho letto per la terza volta in quattro ore e mezza...
Quale è, allora, l'interpretazione giusta? Vorrei capire, perché la questione ha incuriosito anche me. Io, probabilmente, avrei usato subito un unsigned long, ma poi avrei provato a usare anche un int, non essendo sicuro che il long fosse necessario...
Il problema vero è che c'è di mezzo il preprocessore C++, certo c'è una variabile non const ma è locale e allora il preprocessore ha tutto quello che serve per effettuare una operazione a compile time. Stessa cosa quando specifichiamo UL e il preprocessore che comprende questo "qualificatore".
Mentre run-time non c'è garanzia che il calcolo venga effettuato in base al tipo di variabile usato come argomento, per cui un cast esplicito è sempre una garanzia di funzionamento e soprattutto esplicita le intenzioni (informazione utile anche a chi legge il programma).
Riguardo ai warning non è detto che siano abilitati, anzi in arduino IDE li ho trovati sempre disabilitati.
pensavo (speravo) che fosse demandato a delay il cast della variabile.
Pensa sempre il contrario, il compilatore fa (in modo ossessivo) quello che gli dici di fare e non sa leggerti nella mente al fine di verificare se volevi una troncatura o meno.
L'interpretazione giusta è la tua verde ovvero la variabile tempo definita short contiene 60. Questa viene moltiplicata per 1000. Anche mille è un valore a cui il compilatore assegna un tipo che non sarà di certo un unsigned long perché se lo facesse (il compilatore) sprecherebbe byte. Quindi vengono moltiplicati due valori il cui risultato non rientra nel tipo utilizzato causando l'overflow che porta il primo bit a sinistra a 1 (negativo quindi) con un valore di -5536.
Successivamente questo valore viene assegnato al parametro del delay che invece è dichiarato unsigned long, vado a memoria non ho modo di provarlo, che dovrebbe assumere il valore 429496176.
Se tu provi ad usare int non cambia nulla, su Arduino UNO è uguale a short come capacità di contenere valori, sulla due invece cambierebbe il risultato. Usando l'unsigned int invece tutto funzionerebbe
Da questo è dipeso l'allagamento.
Non viene fatto alcun "cast" ... semplicemente prende un int (tempo) lo motiplica per un altro int (60) ed il risultato a sua volta è un int ... che, ovviamente, va solo da -32768 a +32768 (quindi se si va in overflow, il risultato viene troncato).
Fatto questo, il suddetto risulato int (16 bit) viene preso e viene copiato in uno spazio che può contenere 32 bit, unsigned long (il parametro della delay), e viene chiamata la funzione ... tutto qui.
I "cast" impliciti avvengono solo in particolari casi ed in particolari assegnazioni, in tutti gli altri casi, il compilatore rispetta i tipi dichiarati ed esegue le operazioni con le lunghezze dichiarate.
Per cui ... dichiarate sempre il tipo di dato che utiluzzate in funzione anche del risultato che volete !!!
Datman:
e... anche
unsigned long tempo = 80*1000; ...
... si, è un errore tipico in cui è piuttosto facile cadere ... 80 è un char, 1000 è un int, viene promosso il char ad int e viene fatta la moltiplicazione tra i due int che ... ovviamnete va in oveflow e viene troncata
Anche qui, con le costanti, sempre meglio specificare il tipo che si vuole esse abbiano, così non ci sono equivoci ed il compilatore sa come comportarsi
Maurotec:
mmmmh... fatto lo stesso ragionamento ma ho avuto qualche dubbio ....
NO, su Arduino (MCU a 8 bit il cui int è a 16 bit) NON funziona come dici tu, ma tronca ...
void setup() {
// put your setup code here, to run once:
uint32_t tmp;
//
delay(500);
Serial.begin(115200);
tmp = 80 * 1000;
Serial.print("Valore tmp = ");
Serial.println(tmp);
}
void loop() {
// put your main code here, to run repeatedly:
}
Maurotec:
Il PC li considera entrambe di tipo int per cui stampa 80000.
... certo perché il "int" su PC è implementato a 32 bit
Per questo io NON uso mai char, int, long, ecc. ma sempre le definizioni con uint8_t, int16_t, uint32_t, ecc. ... almeno so esattamente di cosa parlo e non mi affido all'implementazione fatta da qualcuno sulla specifica macchina!
Esiste anche un int con QI a 32 ? non molto intelligente
Scusate, non ho resistito ;D
Cercando sulla rete pare che alcuni compilatori, quando richiesto di compilare a 64 bit, usano un long a 8 byte, mentre int rimane a 4 byte. Ovviamente come scrive Guglielmo, meglio usare tipi esplicativi come uint8_t
P.S. personalmente l'ho sempre reputata una "puttanata" il fatto che l'int non abbia dimensione fissa ma solo un size minimo, poi a seconda di architettura/compilatore può essere 16 o 32 bit. Non era meglio un size fisso ? Parere mio, ovvio.
Ricordo che nel compilatore ci sono una marea di flag per modificare il comportamento di default, ad esempio
io uso -funsigned-char per AVR-C, mentre per AVR-C++ resto compatibile con le flags di arduino IDE.
Scusate, non ho resistito
Al massimo un QI=64 che è sempre meglio di 32 ma di più non se po
Nonostante un QI così basso, fa la moltiplicazione corretta! Il compilatore di Arduino, invece, non si pone il problema...
"Scusate, non ho resistito!"