Delay ed unsigned long

Salve a tutti

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);

però mi sembra "strano".

grazie e saluti

Semplice è andato in overflow numerico, short supporta fino a 32767 tu chiedi di fare 60*1000 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 tempo*1000UL forzi comunque il tipo corretto senza il difetto riscontrato e senza cambiare il tipo alla variabile

fabpolli: Semplice è andato in overflow numerico

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... :(

Corretto parzialmente, preferisco la mia versione :)

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.

Ciao.

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 [u]copiato[/u] 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 !!!

Guglielmo

Grazie :)

e… anche
unsigned long tempo = 80*1000;
sarebbe sbagliato, vero? Dovrei scrivere:

unsigned long tempo = 80UL*1000;

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 ;)

Guglielmo

tra i due int che … ovviamnete va in oveflow e viene troncata

mmmmh… fatto lo stesso ragionamento ma ho avuto qualche dubbio e avendo L’IDE aperto (no arduino C++/QtCreator) ho voluto provare.

Il PC li considera entrambe di tipo int per cui stampa 80000.
PS: scusate per il codice non arduinesco, spero si capisca.

    quint32 u = 80*1000;
    float f = 1 / 80;
    qDebug() << u << f;

Stampa:

80000 0

Mentre se 1 lo casto implicitamente come 1.0 stampa

80000 0.0125

In sostanza è sufficiente che un operando sia del tipo grande abbastanza per contenere il risultato della operazione.

Ciao.

int di Arduino Uno è a 16 bit

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:

}

... e vedrai che stampa 14464 :smiling_imp:

Guglielmo

Maurotec: Il PC li considera entrambe di tipo int per cui stampa 80000.

... certo perché il "int" su PC è implementato a 32 bit :smiling_imp:

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!

Guglielmo

Maurotec: quint32 u = ...

Esiste anche un int con QI a 32 ? non molto intelligente :grin: :grin: 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.

… e vedrai che stampa 14464

Ecco lo vedi da dove è nato il dubbio, avrò qualche volta commesso su avr un errore simile e tra architetture mi sono perso.

Vabbe l’avevo fatto anche con variabili a 16 bit lo posto

    quint16 ottanta = 80;
    quint16 mille = 1000;

    
    quint32 mila80 = ottanta * mille;
    
    qDebug() << mila80;

Stampa correttamente:

80000

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

:smiley:
Al massimo un QI=64 che è sempre meglio di 32 ma di più non se po :slight_smile:

Ciao.

Nonostante un QI così basso, fa la moltiplicazione corretta! Il compilatore di Arduino, invece, non si pone il problema…
“Scusate, non ho resistito!” :slight_smile: