Arduino sbaglia una somma?!

Buonasera ragazzi. Il titolo è molto strano lo so, ma il problema pure.
Sto scrivendo il codice di una funzione che ha il compito di prendere un array di int e “incollare” tutti i numeri ritornando un long. Se per esempio il mio array di int contiene {1, 2, 3, 4}, il long finale dovrebbe essere 1234. Ecco la funzione che ho scritto:

long commandArrayToLong(int *arr){
  long temp = 0;
  
  for(int i=0;i<getCommandSize(arr);i++){
    temp = temp + (arr[i] * pow(10, i));
    lcd.print(arr[i]);
    lcd.print(" ");
    lcd.print(temp);
    delay(2000);
    lcd.clear();
  }

  return temp;
}

Mentre la funzione getCommandSize() che vedete è la seguente e si basa sul presupposto che ogni array che passerò ad essa sarà preventivamente concluso con il numero ‘-1’:

int getCommandSize(int *command){
  int s = 0;

  while(command[s] != -1){
    s++;
  }

  return s;
}

La cosa strana è che come risultato la funzione commandArrayToLong(), se arr={1, 2, 3, 4}, ottengo 4319.
Stampando su un lcd il valore che assume di volta in volta la moltiplicazione

arr[i] * pow(10, i)

ottengo i giusti numeri, ovvero 1, 20, 300, 4000. Ma stampando ad ogni ripetizione del ciclo for() l’ammontare di temp ottengo: 1, 21, 320, 4319. Come se superato il secondo ciclo togliesse 1 ogni ciclo aggiuntivo. Non riesco a capire davvero quale possa essere il problema! Grazie in anticipo per l’aiuto.

Se provi a fare così cosa ottieni?

long app = arr[i] * pow(10, i);
temp = temp + app ;

Stesso risultato. Ho provato ad aggiungere un elemento all'array. Stesso risultato, continua a togliere una unità dalla somma totale.

:o cioè se tu stampi la variabile app adesso vedi il risultato che ti aspetti ma alla fine in temp non hai la somma corretta?
Avrei giurato fosse un problema di tipizzazione o Undefined Behiavor c'è qualcosa che mi sfugge.

Prova a scomporre ancora di più:

long powerPart = pow(10, i);
long app = arr[i] * powerPart;
temp = temp + app ;

e a stampare le singole variabili

fabpolli:
:o cioè se tu stampi la variabile app adesso vedi il risultato che ti aspetti ma alla fine in temp non hai la somma corretta?
Avrei giurato fosse un problema di tipizzazione o Undefined Behiavor c'è qualcosa che mi sfugge.

Esatto. Se aggiungo un elemento all'array, tipo '5', la somma dovrebbe essere 54321, invece mi viene fuori 54318, e se stampo la somma ad ogni ciclo vedo che la somma totale di volta in volta è di 1 inferiore a quanto dovrebbe essere. Sembra una macumba, giuro.

Ho provato la cosa con un compilatore C Standard e il problema non si presenta, non ho Arduino per fare la prova diretta

Occhio che pow() ritorna un double, che in arduino è in realtà un float e potrebbe essere un problema di scarsa precisione del double moltiplicato int (contenuto singola cella array)

Io non userei pow() ma una variabile long Potenza che inizializzi a 1 e poi moltiplichi per 10 per avere le potenze del 10

eventualmente prova un cast da int a double
temp = temp + ( double(arr[ i ]) * pow(10, i));

OK! Stampando solo il risultato della potenza ottengo in ordine 1, 10, 99, 999, 9999...
Quindi effettivamente è un problema di precisione. Ma come faccio a renderlo più preciso? Esiste qualche altra funzione che calcola la potenza (al limite me la scrivo io)?

Ok mi sono scritto una funzione per fare la potenza in base 10 e ora funziona tutto.

Tu prima dici:

militandri:
Se per esempio il mio array di int contiene {1, 2, 3, 4}, il long finale dovrebbe essere 1234.

poi dici:

La cosa strana è che come risultato la funzione commandArrayToLong(), se arr={1, 2, 3, 4}, ottengo 4319.

E fin qui ok, diciamo che è questa l'anomalia. Ma poi:

Stampando su un lcd il valore che assume di volta in volta la moltiplicazione
arr * pow(10, i)
ottengo i giusti numeri, ovvero 1, 20, 300, 4000.[/quote]
E qui non mi ritrovo, in che senso sono i "giusti" numeri? Se tu ti aspetti 1234 dovresti avere 4, 30, 200 e 1000! Con 1, 20, 300, 4000 avresti 4321 che non è quello che dicevi all'inizio di attendere come risultato per cui o hai scritto male all'inizio, oppure stai facendo confusione...
Quindi ora sintetizziamo, così magari ti preparo una funzione apposita: se il tuo array è:
int arr[4] = {1,2,3,4};
Vuoi ottenere da questo il valore long 1234 o 4321?

militandri:
Ok mi sono scritto una funzione per fare la potenza in base 10 e ora funziona tutto.

Scusa ho letto solo dopo questa cosa. Puoi postare la tua soluzione, per curiosità personale?

Ma sempre per mia curiosità, a me pare che sia possibile un modo forse più ottimizzato per fare quello che credo (spero) di aver capito, che non calcolare le potenze. Per cui se mi confermi che hai in ingresso un array di int che dovrebbe rappresentare un valore float, mi piacerebbe cercare di capire quale sia una eventuale soluzione alternativa e possibilmente più efficiente possibile.
Non per dirti come farlo, ma a me piacciono le sfide specialmente se tecniche :wink:

PS ma in ogni caso perché un array di di int? Perché non byte visto che ognuno dovrebbe valere tra 0 e 9 visto che poi va composto in un valore decimale? E perché float se sono valori interi?

EDIT: ad esempio qui converto un array di int in un long, senza usare alcun elevamento a potenza per cui l’array di int terminato con -1 mi diventa il valore long 12.345:

int arr[] = {1,2,3,4,5,-1};

void setup() {
  Serial.begin(9600);
  long valore = arrayToLong(arr);
  Serial.print("valore=");
  Serial.println(valore);
}

void loop() {
  
}

long arrayToLong(int arr[]){
  long temp = 0;
  for(int i=0;arr[i]>=0;i++)
    temp = temp*10 + arr[i];
  return temp;
}

docdoc:
Tu prima dici:
poi dici:E fin qui ok, diciamo che è questa l’anomalia. Ma poi:E qui non mi ritrovo, in che senso sono i “giusti” numeri? Se tu ti aspetti 1234 dovresti avere 4, 30, 200 e 1000! Con 1, 20, 300, 4000 avresti 4321 che non è quello che dicevi all’inizio di attendere come risultato per cui o hai scritto male all’inizio, oppure stai facendo confusione…

Quindi ora sintetizziamo, così magari ti preparo una funzione apposita: se il tuo array è:
int arr[4] = {1,2,3,4};
Vuoi ottenere da questo il valore long 1234 o 4321?

Scusa docdoc, mi sono reso conto in seguito di aver scritto nell’ordine sbagliato i numeri. Ho scritto che mi sarei aspettato 1234, invece mi aspettavo 4321. Ma alla fine cambia solo in da che parte scorro l’array con il for.

docdoc:
Scusa ho letto solo dopo questa cosa. Puoi postare la tua soluzione, per curiosità personale?

Certo. Ecco:

long commandArrayToLong(int *arr){
  long temp = 0;
  
  for(int i=0;i<getCommandSize(arr);i++){
    temp = temp + (arr[i] * powTen(i));
  }

  return temp;
}

E la funzione powTen():

long powTen(int n){
  long temp = 1;

  if(n == 0){
    return 1;
  }

  for(int i=0;i<n;i++){
    temp = temp * 10;
  }

  return temp;
}

Comunque nel tuo algoritmo non ho capito come hai impostato il ciclo for. Come funziona quel for(i=0;arr*>=0;i++)?*

boh

io vedo una gigantesca ucas, sono il solo?

unsigned long int commandArrayToLong(int * elenco) //perché int? al massimo fa 9, basta byte
{ 
   unsigned long int returnvalue=0;
   while (*elenco>-1) // fino al tappo
   {
      returnvalue=returnvalue * 10 + *elenco;
      elenco++;
   } 
   return returnvalue;
}

4 righe in croce, niente inutile funzione che conta gli elementi e niente calcolo con pow()
tutto in aritemetica intera e si ha tutto sott’occhio

se poi non “ci piacciono” i puntatori basta usare array e indici, che sono esattamente la stessa cosa

A me non piace un altra cosa: usare -1 come fine dell'array. L'unico tipo di array per cui è prescritto che ci sia un carattere terminatore sono le stringhe (array di char). Ovviamente io posso costruirmi un programma che lo usi, Ma se poi mi dimentico questa specifica posso avere un programma che continua a leggere memoria fino a quando non trova un -1. Quindi affiancherei al controllo attuale anche quello di non aver superato la lunghezza dell'array, magari inserendo un messaggio di errore se, una volta arrivato alla lunghezza massima non ho ancora trovato -1.

Ducembarr:
4 righe in croce, niente inutile funzione che conta gli elementi e niente calcolo con pow()
tutto in aritemetica intera e si ha tutto sott'occhio

se poi non "ci piacciono" i puntatori basta usare array e indici, che sono esattamente la stessa cosa

Caro Ducembarr la funzione che hai postato funziona a meraviglia. Fare *elenco corrisponde a fare elenco[0] se non ho capito male, ovvero punti al primo elemento e poi con elenco++ al secondo e così via. Fantastico non ci avevo pensato.

In ogni caso ci tengo a precisare che sono solo un amatore, di programmazione ne so quanto mi basta per divertirmi e chiaramente alcune soluzioni "da programmatore" non mi vengono, di conseguenza alcune soluzioni che trovo potrebbero sembrare troppo dispersive.

militandri:
Ho scritto che mi sarei aspettato 1234, invece mi aspettavo 4321. Ma alla fine cambia solo in da che parte scorro l’array con il for.

Ah ok, esatto. Allora ti basta invertire anche il for() che ho scritto.

Comunque nel tuo algoritmo non ho capito come hai impostato il ciclo for. Come funziona quel for(i=0;arr[i]>=0;i++)?

Dato che dal tuo codice ho dedotto che hai un -1 come terminatore del numero, quel for() equivale a:

int i=0;
while (arr[i]>=0){
  temp = temp*10 + arr[i];
  i++;
}

Ossia avanza nell’array e si ferma non appena trova un valore minore di zero.

Ducembarr:
unsigned long int commandArrayToLong(int * elenco) //perché int? al massimo fa 9, basta byte

Si, infatti è quello che avevo detto pure io (“ma in ogni caso perché un array di di int? Perché non byte visto che ognuno dovrebbe valere tra 0 e 9”).
Ma è l’OP che ha probabilmente in ingresso un array di int, per cui mi sono adattato a quello.

returnvalue=returnvalue * 10 + *elenco;
elenco++;
4 righe in croce, niente inutile funzione che conta gli elementi

Attenzione: se hai un puntatore ad intero, che è 2 byte, se lo incrementi di 1 (byte) non leggi l’int successivo ma il secondo byte dello stesso int… :wink: :wink: Quindi hai fatto troppe croci mi sa :smiley: per fare le cose completamente pulite e portabili dovresti per genericità allora usare sizeof(), ma usando le quadre ottieni la stessa cosa senza sbatterti tanto.

Silente:
A me non piace un altra cosa: usare -1 come fine dell’array. L’unico tipo di array per cui è prescritto che ci sia un carattere terminatore sono le stringhe (array di char). Ovviamente io posso costruirmi un programma che lo usi, Ma se poi mi dimentico questa specifica posso avere un programma che continua a leggere memoria fino a quando non trova un -1. Quindi affiancherei al controllo attuale anche quello di non aver superato la lunghezza dell’array, magari inserendo un messaggio di errore se, una volta arrivato alla lunghezza massima non ho ancora trovato -1.

Hai perfettamente ragione, ed anche io in genere faccio così, ma, come ho detto pure a ducembarr, l’ho preso come dato di fatto viste le condizioni poste dall’OP quindi non ho ritenuto opportuno cambiargli quella logica senza conoscere il resto del suo programma ossia né come sia definito sto benedetto array né come ottenga quei valori.

docdoc:
Attenzione: se hai un puntatore ad intero, che è 2 byte, se lo incrementi di 1 (byte) non leggi l'int successivo ma il secondo byte dello stesso int... :wink: :wink: Quindi hai fatto troppe croci mi sa :smiley: per fare le cose completamente pulite e portabili dovresti per genericità allora usare sizeof(), ma usando le quadre ottieni la stessa cosa senza sbatterti tanto.

ma cosa stai dicendo?

a parte che il fatto che funzioni (la hai provata? evidentemente no, altrimenti non avresti scritto quello che hai scritto) dimostra che ti sbagli

ma comunque un minimo di aritmetica dei puntatori servirebbe di conoscerla........

docdoc:
Hai perfettamente ragione, ed anche io in genere faccio così, ma, come ho detto pure a ducembarr, l'ho preso come dato di fatto viste le condizioni poste dall'OP quindi non ho ritenuto opportuno cambiargli quella logica senza conoscere il resto del suo programma ossia né come sia definito sto benedetto array né come ottenga quei valori.

Allora. Il programma legge dei bytes dalla porta seriale, bytes inviati da computer e che corrispondono a delle coordinate spaziali. Dal momento che ho creato una funzione che legge tali byte e che ritorna l'array con tutti i bytes letti, non volevo delegare ad una variabile esterna alla funzione stessa il compito di conservare la lunghezza dell'array, dal momento che avrebbe potuto portare a confusione nel momento in cui mi sarei ritrovato con più di un array dello stesso tipo.
La soluzione del -1 finale non convince neanche me, ma non saprei come altro risalire al numero di elementi nell'array. Avevo provato con sizeof(array)/sizeof(array[0]), ma a quanto ho capito sizeof(array) ritornerebbe soltanto la dimensione del puntatore. Non sapevo come altro fare e mi sono inventato sta cosa qua.
Se avete suggerimenti per migliorare anche questo aspetto sono ben accetti!