Sari la conținut
ELFORUM - Forumul electronistilor

Conversie valoare float la string pentru lcd


M.Adrian

Postări Recomandate

       Salut, tot caut de ceva timp solutii pentru afisarea unei valori de tip float pe un lcd alfanumeric 1602. Am incercat cu functiile ftoa() si sprintf() dar pe display e afisata valoarea urmata de 5 sau 6 zecimale, chiar daca eu declar acea variabila cu 3 sau alt numar de zecimale.


    char *buffer;
    int status;
    float x=50.123;
    LCD_SetCursor(1,2);
    buffer=ftoa(x,&status);
    LCD_Putstring(buffer);

 

 

      

lcd.JPG

       Asa e afisata valoarea x, cu inca 3 zecimale dupa ea, folosind functia ftoa().

 

   char buffer[4];
   float x=50.123;
   LCD_SetCursor(1,2);
   sprintf(buffer,"%f",x);
   LCD_Putstring(buffer);

image.thumb.png.d65fe66152a0a89ee4fb977353016c1a.png

       Asa e afisata valoarea pe lcd cu functia sprintf(), cand marimea de la buffer e 4, eu am crezut ca marimea de aici e numarul de zecimale care va fi afisat, dar nu e asa, in programul meu, pentru orice valoare >=4 va afisat numarul ca in poza de mai sus, iar daca marimea bufferului e 3, atunci numarul e afisat doar cu 2 zecimale, daca marimea bufferului e 2 sau 1 atunci nu mai apare decat punctul dupa 50.

image.thumb.png.fe89937a3b3e0f55797c96b859ced592.png

 

       Aa si mai e o ciudatenie, daca scriu acelasi cod folosind sprintf(), dar il scriu ca o functie in biblioteca de la lcd si apelez acea functie in main(), parca ignora complet dimensiunea bufferului iar cand apelez functia imi afiseaza valoarea ca in prima poza, adica cu 6 zecimale dupa ea.

       

void LCD_PrintFloat(float valoare)
{
    char buffer[3];
    sprintf(buffer,"%f",valoare);
    LCD_Putstring(buffer);
}

 

       Cum as putea sa rezolv problema? Inafara de procesarea valorii prin operatii de tip % sau / in stil clasic. Ce vreau e sa pot afisa valori cu zecimale, dar cu un numar predeterminat de zecimale, in sensul ca daca calculez o valoarea si in urma calculului are 5 zecimale dar eu vreau sa afisez doar 3, sa pot face asta.

Editat de M.Adrian
Link spre comentariu
  • Răspunsuri 30
  • Creat
  • Ultimul Răspuns

Top autori în acest subiect

  • Liviu M

    10

  • M.Adrian

    10

  • bcristian

    2

  • mars01

    2

Top autori în acest subiect

Imagini postate

Salut!

 

Poti incerca asa:

 

char buffer[20];

float x=50.123;
sprintf(buffer,"%.3f",x);

LCD_SetCursor(1,2);
LCD_Putstring(buffer);

 

Nu este cea mai eficienta metoda, dar functie de compilator ar trebui sa iti ofere ce ai nevoie.

PS: dimensiunea bufferului este pentru toate caracterele care formeaza un numar float (daca are minus in fata, punctul separator, partea intreaga, zecimalele). Fiecare digit ocupa un byte din buffer, fie ca este din partea intreaga sau din zecimale.

 

Documentatie sprintf:

https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm

Editat de mars01
Link spre comentariu
1 oră în urmă, mars01 a spus:

Salut!

 

Poti incerca asa:

 

char buffer[20];

float x=50.123;
sprintf(buffer,"%.3f",x);

LCD_SetCursor(1,2);
LCD_Putstring(buffer);

 

Nu este cea mai eficienta metoda, dar functie de compilator ar trebui sa iti ofere ce ai nevoie.

PS: dimensiunea bufferului este pentru toate caracterele care formeaza un numar float (daca are minus in fata, punctul separator, partea intreaga, zecimalele). Fiecare digit ocupa un byte din buffer, fie ca este din partea intreaga sau din zecimale.

 

Documentatie sprintf:

https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm

       A mers asa, multumesc! Acum ramane intrebarea de unde apareau zecimalele alea in plus.

Link spre comentariu
Acum 3 ore, M.Adrian a spus:

     Acum ramane intrebarea de unde apareau zecimalele alea in plus.

Se știe că reprezentarea float nu este o reprezentare exactă ci doar aproximativă. Probabil că valoarea introdusă de tine 50.123 a fost convertită de compilator în cea mai apropiată valoare float care ar fi (în zecimal) 50.123046 . La convertirea înapoi în zecimal a valorii float respective algoritmul a returnat toate zecimalele disponibile. Așa văd eu lucrurile (cel puțin la ora asta ...).

Link spre comentariu
Acum 22 ore, M.Adrian a spus:
char buffer[4];
   float x=50.123;
   LCD_SetCursor(1,2);
   sprintf(buffer,"%f",x);

Atentie pe viitor si la dimensiune buffer[].
x=50.123 nu are cum sa incapa in 4 octeti dupa conversia din float in string cu sprintf().
Ai o frumusete de buffer overflow mai sus. Si compilatorul nu prea semnalizeaza asta. Si uneori nici in executie nu se manifesta mereu.
Poti avea probleme foarte greu de depistat in astfel de cazuri.
 

Editat de Vizitator
Link spre comentariu
Acum 6 ore, Liviu.Mihaiu a spus:

Atentie pe viitor si la dimensiune buffer[].
x=50.123 nu are cum sa incapa in 4 octeti dupa conversia din float in string cu sprintf().
Ai o frumusete de buffer overflow mai sus. Si compilatorul nu prea semnalizeaza asta. Si uneori nici in executie nu se manifesta mereu.
Poti avea probleme foarte greu de depistat in astfel de cazuri.
 

       E bine de retinut asta pe viitor. Pot sti cumva orientativ cam cat ar trebui sa fie dimensiunea bufferului ca sa nu fie probleme, sa zicem in cazul in care afisez o valoare maxima de tipul 999.999? Daca raspunsul se afla in documentatia de la @mars01 pentru sprintf() atunci sa imi fie cu iertare dar inca nu am apucat sa o citesc toata pe indelete. 

Link spre comentariu

Poti utiliza functia snprintf() pentru a afla dimensiunea minima pentru buffer.
Si apoi sa aloci dinamic spatiu pentru buffer:


   float my_pi=333.141592;

   int size = snprintf(NULL, 0, "%f", my_pi); 

   char* buffer = malloc(size + 1);                // +1 pentru terminatorul '\0'

Dar nu cred ca este nevoie de complicatia asta.
 

Editat de Vizitator
Link spre comentariu

       E o varianta buna dar in general nu cred ca voi avea nevoie de alocare dinamica de memorie, totusi cred ca e o idee buna sa pot afla cu snprintf dimensiunea pentru buffer si sa o setez fix din start. 

       Multumesc pentru sfaturi! 

Link spre comentariu
Acum 5 ore, Liviu.Mihaiu a spus:

Poti utiliza functia snprintf() pentru a afla dimensiunea minima pentru buffer.
Si apoi sa aloci dinamic spatiu pentru buffer.

 

Alocarea dinamica este periculoasa pentru acele uC-uri care au o cantitate de memorie redusa pentru ca se creeaza iluzia ca exista suficienta memorie si in anumite conditii se poate ajunge la erori de procesare datorate unui consum mai mare de RAM decat exista (numai vorbim de posibila fragmentare a memoriei care conduce la o pierdere si mai mare a memoriei RAM disponibile).

Asa ca mai bine se aloca static de la inceput o valoare maxima a buffer-ului, valoare care se poate seta printr-o directiva #define de ex pentru a face aceasta configurabila.

// poate contine un numar real maxim 17digiti in partea intreaga + partea fractionara
// din cele 3 elemente ramase avem un posibil semn minus, punctul separator
// si caracterul 'NULL' care incheie orice string
#define BUFF_LENGTH		20	

char buffer[BUFF_LENGTH];

 

**********************************************************************************************************************************************

 

Citat

E bine de retinut asta pe viitor. Pot sti cumva orientativ cam cat ar trebui sa fie dimensiunea bufferului ca sa nu fie probleme, sa zicem in cazul in care afisez o valoare maxima de tipul 999.999?

 

In acest caz se poate calcula simplu: 1 element pt un posibil semn minus + 3 elemente pentru partea intreaga (999) + 1 element pentru punctul zecimal + 3 elemente in partea fractionara (999) + 1 element pt terminatorul 'NULL' = 9.
 

Prin urmare ar fi suficient:

#define BUFF_LENGTH	9

char buffer[BUFF_LENGTH];

 

Editat de mars01
Link spre comentariu

Acum, că ai primit răspunsul pe care îl doreai, putem despica puțin firul în 4.

Din câte știu eu, sprintf (și similarele) consumă multe resurse. Într-un topic mai vechi al lui Mircea am discutat despre o abordare mai puțin generală dar mai economică. Poate că ți se potrivește? 

Editat de Liviu M
Link spre comentariu

Ca principiu, sprintf consuma semnificativ mai multa putere de calcul decat ftoa. Evident, este foarte improbabil ca asta sa fie o problema practica daca vorbim de afisare, care nu are rost sa fie facuta la frecvente mari, oricum.

Pe de alta parte, daca platforma nu are deja facuta o functie care sa permita conversia in diverse reprezentari si precizii, folosirea printf e preferabila. Mai ales daca intrebarea e pusa de la nivelul initiatorului. Conversia float > string e semnificativ mai complicata decat pare, un exemplu aici.

 

Mai important insa e altceva - evitarea buffer overflow. Este o greseala foarte usor de facut si cu consecinte potential catastrofale, si mai ales greu de identificat.

sprintf, strcpy, si de altfel mai toate functiile "clasice" care scriu in stringuri/buffere sunt foarte pacatoase din punctul asta de vedere, si in standardele actuale sunt depasite, si majoritatea compilatoarelor te si avertizeaza sa nu le folosesti.

Exista acum (de mai bine de 10 ani de fapt) variante sigure ale lor, in care trebuie sa specifici si lungimea sirului in care scrii. Pt C++ si array-uri statice existe variante care deduc singure informatia.

 

De exemplu, daca vrem sa scriem intr-un buffer fix, avem snprintf, care se asigura ca nu scriem aiurea prin memorie.

Link spre comentariu

Depășirea buffer-ului, sprintf/snprintf... fuseseră deja lămurite. 

În rest, părerea mea e că orice soluție "de biblioteca" (adică care rezolvă orice variantă posibilă de intrare) nu poate fi decât mai puțin eficientă decât variante dedicate (compuse din teste, scăderi și adunări), da' o schimb cu plăcere dacă cu argumente convingătoare. :)

Metoda pe care am menționat-o anterior nu-mi aparține, e plagiată de pe forumul uChip. 

Link spre comentariu

Creează un cont sau autentifică-te pentru a adăuga comentariu

Trebuie să fi un membru pentru a putea lăsa un comentariu.

Creează un cont

Înregistrează-te pentru un nou cont în comunitatea nostră. Este simplu!

Înregistrează un nou cont

Autentificare

Ai deja un cont? Autentifică-te aici.

Autentifică-te acum



×
×
  • Creează nouă...

Informații Importante

Am plasat cookie-uri pe dispozitivul tău pentru a îmbunătății navigarea pe acest site. Poți modifica setările cookie, altfel considerăm că ești de acord să continui.Termeni de Utilizare si Ghidări