Un bug del compilatore?

Tie', beccatevi sto codice e provate cosa succede:

/*
Test by Ugo 26/09/2011
*/

unsigned long goo=60;

void setup()
{
Serial.begin(57600);
Serial.println("\nVediamo che succede\n");

for(int i=0; i<20; i++)
{
Serial.print( foo() );
Serial.println("<-");
};

Serial.end();
}

void loop()
{
}

unsigned long foo()
{
static int a;
static int b = 5;
static int c = 9;
static unsigned long d;

a = int(goo++);

Serial.print("->");
Serial.print( a );
Serial.print("<->");
d = c3600+ b60 +a;

return( d ) ;
}

Ci ho perso tutto il w.e. per isolare sto tratto di codice per capire dove s'impallava il programma:
]:slight_smile:

Bye,
Ugo

un bug nelle tue conoscenze, trattasi di semplice overflow delle variabili int... :grin:

maggiori info:
http://arduino.cc/en/Reference/Int

lesto:
un bug nelle tue conoscenze, trattasi di semplice overflow delle variabili int... :grin:

maggiori info:
http://arduino.cc/en/Reference/Int

Stavolta non sono d'accordo. Qualsaisi compilatore serio alloca spazio per la variabile di destinazione, dove va a mettere i risultati, non viceversa.
So' niubbo d'arduino, ma compilavo in C in tempi che sicuro te non eri ancora nato :wink:

Bye,
Ugo

ma stai scherzando? TUTTI i linguaggi che conosco soffrono di overflow di variabile!
trovamene uno che non abbia questo problema, non dico che sia impossibile ma molto, molto difficile.

Altrimenti mi spieghi che differenza c'è tra dichiarare un int o un long o un byte?

e tra un float e un double?

lesto:
ma stai scherzando? TUTTI i linguaggi che conosco soffrono di overflow di variabile!
trovamene uno che non abbia questo problema, non dico che sia impossibile ma molto, molto difficile.

Altrimenti mi spieghi che differenza c'è tra dichiarare un int o un long o un byte?

e tra un float e un double?

No, no, aspetta, non fraintendere.
Un conto e' l'overflow e un conto e' l'allocazione di memoria.
In tutti i compilatori che ho usato, tanti, ma mille anni fa, il problema che ho riscontrato qui non esisteva. E spiego meglio:

Se alloco
byte a;

e poi ci vado a mettere dentro
a=300;
Ovvio che vado fuori, in overflow.

se alloco
byte a;
int b = 300;
va bene, ovviamente.

e' evidente che se scrivo questo:
byte a;
int b;
...
b= int(300);
Va ancora bene ma e' une ridondanza inutile, visto che in "b" ho allocato il giusto spazio e "a" e' allocata

Da qui, e' evidente che
byte a;
int b;
...
b= 300a;
dovrebbe essere giusto.
Ho capito che il compilatore di arduino non lo fa, pero' a questo punto si comporta in modo diverso
Perche' se e' giusto
b = int(300
a);
Allora DEVE essere NECESSARIO e non opzionale anche
b=int(a);

Cia'

ah, ok, ora ho capito quello che intendi, ma sia il C, che il C++, che java, che php che python.... e posso ancora andare avanti... non ragionano così.

d = c3600+ b60 +a;

vuol dire: prendi C (che è int) e moltiplicalo con 3600 (sempre int), il risultato sarà messo in un int, essendo int la variabile "più grossa" (fregandosene bellamente dell'overflow), etc... alla fine il risultato verrà convertito in un long

puoi forzare, per esempio, 3600 ad esere long scrivendo
3600L

quindi
d = c3600+ b60 +a;
vuol dire: prendi C (che è int) e moltiplicalo con 3600 (ora è long), il risultato sarà messo in un long, essendo long la variabile "più grossa" (fregandosene bellamente di eventuali overflow), etc.

lelle persa

d = c3600L+ b60 +a;

Ciao Uwe

lesto:
ah, ok, ora ho capito quello che intendi, ma sia il C, che il C++, che java, che php che python.... e posso ancora andare avanti... non ragionano così.

d = c3600+ b60 +a;

vuol dire: prendi C (che è int) e moltiplicalo con 3600 (sempre int), il risultato sarà messo in un int, essendo int la variabile "più grossa" (fregandosene bellamente dell'overflow), etc... alla fine il risultato verrà convertito in un long

puoi forzare, per esempio, 3600 ad esere long scrivendo
3600L

quindi
d = c3600+ b60 +a;
vuol dire: prendi C (che è int) e moltiplicalo con 3600 (ora è long), il risultato sarà messo in un long, essendo long la variabile "più grossa" (fregandosene bellamente di eventuali overflow), etc.

Buono a sapersi, non ci avevo mai pensato..... Sono quelle cose che ti fanno perdere ore di lavoro per una cavolata...

E' la conversione implicita dei tipi di dato.
I calcoli vengono eseguiti usando il tipo di dati più grande tra quelli dell'operazione, ma il risultato viene poi riconvertito nel tipo di dati della variabile di destinazione.

Provate questo sketch:

void setup() {
    delay(2000);
    Serial.begin(19200);
}

void loop() {
    byte a;
    
    a=1500*3500;
    Serial.println(a, DEC);
    delay(2000);
}

Sul terminale seriale apparirà scritto 208. Nessun errore da parte del compilatore, nessun comportamento strano da parte del micro. Semplicemente, del risultato vengono presi i primi 8 bit, quelli cioè che stanno in un tipo di dati byte, nonostante i calcoli siano eseguiti a 32 bit.

Si, questo lo so, ma cosa succede se fai questo?

void setup() {
    delay(2000);
    Serial.begin(19200);
}

void loop() {
    byte a = 200;
    int b;
    
    b=a*10;
    Serial.println(b, DEC);
    delay(2000);
}

A quello che ho capito l'operazione a * 10 viene eseguita a 8 bit, generando un overflow, e il risultato (sbagliato) viene messo in un int, giusto? Purtroppo ora non ho arduino per provare...

No, ottieni 2000.
Questo perché, come ti ho scritto, il compilatore fa sì che l'operazione venga eseguita usando il tipo di dati più grande necessario a contenere i valori in gioco e solo alla fine viene convertito il risultato per il tipo di dato in uso.
Ora, siccome 200*10 va oltre il tipo byte, il compilatore fa usare un int (16 bit), ottieni così 2000 che viene memorizzato in una variabile di tipo int. Siccome int in un int ci sta, non hai nessun "pezzo" perso per la strada.

leo72:
No, ottieni 2000.
Questo perché, come ti ho scritto, il compilatore fa sì che l'operazione venga eseguita usando il tipo di dati più grande necessario a contenere i valori in gioco e solo alla fine viene convertito il risultato per il tipo di dato in uso.
Ora, siccome 200*10 va oltre il tipo byte, il compilatore fa usare un int (16 bit), ottieni così 2000 che viene memorizzato in una variabile di tipo int. Siccome int in un int ci sta, non hai nessun "pezzo" perso per la strada.

lesto:
ah, ok, ora ho capito quello che intendi, ma sia il C, che il C++, che java, che php che python.... e posso ancora andare avanti... non ragionano così.

d = c3600+ b60 +a;

vuol dire: prendi C (che è int) e moltiplicalo con 3600 (sempre int), il risultato sarà messo in un int, essendo int la variabile "più grossa" (fregandosene bellamente dell'overflow), etc... alla fine il risultato verrà convertito in un long

puoi forzare, per esempio, 3600 ad esere long scrivendo
3600L

quindi
d = c3600+ b60 +a;
vuol dire: prendi C (che è int) e moltiplicalo con 3600 (ora è long), il risultato sarà messo in un long, essendo long la variabile "più grossa" (fregandosene bellamente di eventuali overflow), etc.

Chi ha ragione?

leo72:
No, ottieni 2000.
Questo perché, come ti ho scritto, il compilatore fa sì che l'operazione venga eseguita usando il tipo di dati più grande necessario a contenere i valori in gioco e solo alla fine viene convertito il risultato per il tipo di dato in uso.
Ora, siccome 200*10 va oltre il tipo byte, il compilatore fa usare un int (16 bit), ottieni così 2000 che viene memorizzato in una variabile di tipo int. Siccome int in un int ci sta, non hai nessun "pezzo" perso per la strada.

no. concordo sul risultato ma non sul motivo.
scrivere "a10" da per implicito che 10 sia un int, quindi:
int b=a
10;
sarà 2000
se fai
a = a*10;
l'operazione darà 2000 (perchè 10 è un int), ma poi verrà troncata in byte

prova a fare uno skect di esempio:
2000 trocanto in byte credo sia 0 (caso a = a10;)
se invece fai
byte b=10;
byte a=200;
a=a
b; //in modo che entrambi i membri siano forzatatmente in binario

dovresti trovare un risultato diverso, credo (dovrei fare i conti)

Io :stuck_out_tongue_closed_eyes:
No, diciamo tutti e due, lesto non ha spiegato tutto.

Prova questo codice:

void setup() {
    delay(2000);
    Serial.begin(19200);
}

void loop() {
    byte a;
    
    a=15000*35000;
    Serial.println(a, DEC);
    delay(2000);
}

1500035000=525000000, che è un numero a 28 bit. Eppure il terminale stampa ancora un valore coretto.
64
Che è appunto 525000000&255 (ossia i primi 8 bit) del numero.
Se invece provi con
a=150000
350000;
otterrai 0.
questo perché il risultato non può essere contenuto in 32 bit, il tipo di dati più grandi gestibile dall'Atmega e va in overflow, dando come risultato 0. Quindi l'overflow viene ignorato (o per meglio dire non si verifica) solo finché il risultato è contenibile in un dato gestibile dal micro.

attento a scrivere

 a=15000*35000;

non vorrei che il precompilatore ci metta le mani. Prova a mettere il 15000 e/o il 35000 in variabili int. Anzi il 35000 non ci sta in un int, quindi (sempre il precompilatore) potrebbe aggiungere una bella L o una unsigned di soppiatto!

15000*35000=525000000, che è un numero a 28 bit. Eppure il terminale stampa ancora un valore coretto.
64
Che è appunto 525000000&255 (ossia i primi 8 bit) del numero.

se così fosse, ovvero avviene un cast implicito a long, allora ho sbagliato io. Ma non mi spiegherei più perchè il suo programma dà l'verflow a 32000 circa, il limte degli int (vedi sopra la mia possibile spiegazione)

Se invece provi con
a=150000*350000;
otterrai 0.

questo è quello che mi aspetterei di vedere (credo, non ho fatto i conti, e arduino è al momento impegnato)

Dalla discussione animata che ne sta segundo vedo che la cosa non era cosi' banale. XD
Son contento di aver buttato questo sasso nello stagno.

Ugo