serial.print() e notazione scientifica

Salve a tutti.
Mi sono imbattuto in questo problema che mi ha fatto perdere un sacco di tempo.
Ho uno sketch che deve fare dei calcoli in floating point, il cui risultato finale è un numero “ragionevole” (tra circa 500 e 50000, a seconda del parametro d’ingresso) ma i risultati intermedi prevedevano dei numeri del tipo 1E-29 (comunque entro il range del tipo float gestibile da Arduino). Per debuggare il programma mi facevo stampare i valori intermedi con Serial.print(), ma ottenevo una sfilza di 0.00, mentre il risultato finale era corretto. Ho perso un sacco di tempo a capire dove fosse il problema ed ho scoperto che Serial.print ha dei limiti che mi sembrano assurdi.

Print.cpp alla riga 223 dice
(Print.cpp lo si trova in C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino):

size_t Print::printFloat(double number, uint8_t digits)
{
size_t n = 0;

if (isnan(number)) return print(“nan”);
if (isinf(number)) return print(“inf”);
if (number > 4294967040.0) return print (“ovf”); // constant determined empirically
if (number <-4294967040.0) return print (“ovf”); // constant determined empirically

Ci sono quindi due problemi:

  1. printFloat non supporta la notazione scientifica;
  2. 4294967040.0 (~4E9) è ben inferiore al limite dei float a 32 bit supportati da Arduino (~1E38)

C’è qualcuno che ha già visto questo problema e ha trovato una soluzione?

Grazie

Dovresti farti una funzioncina di stampa, qualcosa di simile:

void ingPrint(float num)
{
float num2;
char molt;
if(num>=1000000000){num2=num/1000000000; molt='G';}
else if(num>=1000000){num2=num/0000000; molt='M';}
else if(num>=1000){num2=num/1000; molt='k';}
else if(num<0.0000001){num2=num/1000000000; molt='n';}
...
printFloat...
Serial.print(molt);

}

Vorrei sperare di trovare qualcosa di meglio. Da E-38 a E38 ci sono 76 ordini di grandezza (se poi avessimo a che fare con i double…), senza contare che i prefissi del SI arrivano solo al E+/-24.
Comunque, grazie del suggerimento.

Qui un utente ha scritto una sua funzione: https://forum.arduino.cc/index.php?topic=46931.0

Comunque la risposta di Datman mi ha dato lo spunto… Basta dividere o moltiplicare ripetutamente per 10 il numero in questione, fino a portarlo nell'intervallo 1-10, e questo dà la mantissa, mentre il numero di divisioni o moltiplicazioni dà l'esponente. Una manciata di righe di codice.

Invece, per rispondere a nid69ita, il topic che lui segnala è troppo vecchio per essere la soluzione definitiva. Però l'autore del post, robtillaart, interpellato personalmente, mi segnala che ha sviluppato una libreria che risolve definitivamente e, debbo dire, elegantemente, il problema. Se vi interessa, la trovate qui Arduino/libraries/MathHelpers at master · RobTillaart/Arduino · GitHub
Peccato solo che non sia nel formato standard delle librerie Arduino e bisogna installarla manualmente. E non ha documentazione, ma ci sono solo due esempi che hanno bisogno di qualche aggiustamento per girare su qualsiasi Arduino. Sul Micro su cui l'ho provata bisogna aggiungere il while(!Serial) per poter vedere l'output.

Comunque a questo punto direi che il problema è risolto.

Più semplicemente, fai il logaritmo in base 10 e prendi la parte intera: Quello è l'esponente da indicare sul display ed è la potenza di dieci per cui dividere il numero da visualizzare. Non so, però, se fare il logaritmo è più pesante che fare una fila di if/else, considerando l'ordine di grandezza medio di ciò che verrà visualizzato.