Separare la parte intera e quella decimale di una float in due int

Ciao a tutti, come da oggetto, se ho in una float un valote per esempio di 10,75 vorrei ottenere due variabili int o byte una con valore 10 e l'altra con valore 75. In pratica ho bisogno di conservare tale valore nella eprom di arduino, ma siccome ci posso memorizzare unicamente variabili byte, questo mi sembra l'unico modo di procedere, ma non mi viene proprio in mente come fare =(. A voi viene in mente qualcosa?

Ciao Riccardo.

Ciao, non so se esiste un modo più veloce però a me è ventuo in mente di: 1) Convertire la variabile float in una intera ( variabileIntera = (int) variabileFloat ) 2) Sottrarre alla variabile float la variabile intera ( variabileFloat -= variabileIntera ) 3) Moltiplicare per 100 la variabile float ( variabileFloat *= 100 ) 4) Convertire la variabile float in una nuova variabile itera ( variabileIntera2 = (int) variabileFloat )

Ciao

riciweb: ma siccome ci posso memorizzare unicamente variabili byte, questo mi sembra l'unico modo di procedere, ma non mi viene proprio in mente come fare

Devi memorizzare tutti e quattro i byte che compongono il float scomponendolo tramite un puntatore o tramite una unione. Per riavere il float non fai altro che leggere i quattro byte precedentemente memorizzati e lo ricomponi. Vedi questo thread per ulteriori dettagli.

Grazie a tutti e due, ho dato un'occhiata al thread di astrobeed, ma non ci ho capito molto, sono un pò cotto a dire il vero... domani provo.

Riccardo

fabpolli:
Ciao, non so se esiste un modo più veloce però a me è ventuo in mente di:

  1. Convertire la variabile float in una intera ( variabileIntera = (int) variabileFloat )
  2. Sottrarre alla variabile float la variabile intera ( variabileFloat -= variabileIntera )
  3. Moltiplicare per 100 la variabile float ( variabileFloat *= 100 )
  4. Convertire la variabile float in una nuova variabile itera ( variabileIntera2 = (int) variabileFloat )

Ciao

questo metodo estrae solo 2 cifre dopo la virgola… senza contare l’errore infinitesimale del calcolatore, che potrebbe variare il numero.

per renderlo con più cifre basta usare 10 al punto 3), sootrarre a variabileFloat il valore estratto, e mettere in loop il punto 3 e 4 finchè il valore non è 0. (più o meno, da fare attenzione all’infinitesimale del calcolatore).

Il metodo di Astro invece non lo capisco, anche avendo i bite del float, esso è salvato come 1,XXX*10^Y (attiamlente viene salvato XXXeYYY in forma di byte). quindi il valore non è utilizzabile al nostro scopo se prima non convertito (e tornaimo al punto 1) della lista).

ma magari mi sbaglio, non sarebbe la prima volta, e neanche l’ultima :slight_smile:

[code][quote author=lesto link=topic=77970.msg589627#msg589627 date=1320608093]
[quote author=fabpolli link=topic=77970.msg588872#msg588872 date=1320517794]
Ciao, non so se esiste un modo più veloce però a me è ventuo in mente di:
1) Convertire la variabile float in una intera ( variabileIntera = (int) variabileFloat )
2) Sottrarre alla variabile float la variabile intera ( variabileFloat -= variabileIntera )
3) Moltiplicare per 100 la variabile float ( variabileFloat *= 100 )
4) Convertire la variabile float in una nuova variabile itera ( variabileIntera2 = (int) variabileFloat )

Ciao
[/quote]

questo metodo estrae solo 2 cifre dopo la virgola... senza contare l'errore infinitesimale del calcolatore, che potrebbe variare il numero.

per renderlo con più cifre basta usare 10 al punto 3), sootrarre a variabileFloat  il valore estratto, e mettere in loop il punto 3 e 4 finchè il valore non è 0. (più o meno, da fare attenzione all'infinitesimale del calcolatore).

Il metodo di Astro invece non lo capisco, anche avendo i bite del float, esso è salvato come 1,XXX*10^Y (attiamlente viene salvato XXXeYYY in forma di byte). quindi il valore non è utilizzabile al nostro scopo se prima non convertito (e tornaimo al punto 1) della lista).

ma magari mi sbaglio, non sarebbe la prima volta, e neanche l'ultima :)
[/quote]

da quello che ho studiato di C mi pare di ricordare che:

supponendo che una variabile float occupi 4 byte e una int 2 byte,
dichiarando un'unione del tipo

[code]
union{
    int intero[2];
    float virgola;
}unione;

quando scrivi sulla eeprom scrivi i due interi contenuti nel vettore "intero"; successivamente quando recuperi i dati dalla eeprom li salvi nel vettore e poi scrivendo unione.virgola riesci a recuperare il valore float..

più o meno dovrebbe essere così...

[/code] [/code]

ah ok, non ho letto il messaggio inizile, il metodo di astro allora è valido ed è più o menno la stessa cosa della union. Visto che se non erro la eeprom lavora a byte, meglio utilizzare direttamente essi:

union{ byte b[4]; float virgola; }unione;

ora se assegni un valore a virgola, facendo unione.virgola=XXX.YY, in unione.b

 ottieni il byte corrispondente.

in teoria puoi anche fare (non testato)

byte a*;

float virgola=90.6;

a=&virgola;

ora a è un array di byte, se leggi le prime 4 celle leggi il valore in byte del float

lesto: ah ok, non ho letto il messaggio inizile, il metodo di astro allora è valido ed è più o menno la stessa cosa della union. Visto che se non erro la eeprom lavora a byte, meglio utilizzare direttamente essi:

union{ byte b[4]; float virgola; }unione;

ora se assegni un valore a virgola, facendo unione.virgola=XXX.YY, in unione.b

 ottieni il byte corrispondente.

[/quote] si i byte vanno bene! anche i char si possono usare se non sbaglio...

[quote]

in teoria puoi anche fare (non testato)

byte a*;

float virgola=90.6;

a=&virgola;

ora a è un array di byte, se leggi le prime 4 celle leggi il valore in byte del float [/quote]

anche questo metodo funziona, perchè la variabile float è memorizzata in quattro celle contigue e quindi si può usare l'aritmetica dei puntatori!

ho da poco affrontato il tuo stesso problema e l’ho risolto così: ho trasformato il float in unsigned int

float a = 10,75;
unsigned int b = a*100;

a questo punto hai un numero int di 2 bytes, ne assegni uno alla posizione EEPROM 0 e l’altro alla 1:

  EEPROM.write(0, lowByte(b));
  EEPROM.write(1, highByte(b));

Riottenere il tuo numero è ancora più veloce: leggi prima il byte low e lo sposti di 8 posti a sin e poi lo sommi con OR al byte high

int b = EEPROM.read(1) << 8 | EEPROM.read(0);

Adesso devi solo dividere per 100 per ottenere il float:

float a = b/100;

http://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html

Non c'è bisogno di convertire niente, c'è già la funzione che scrive e legge i float sulla eeprom... non è nel reference dell'Arduino ma basta cercare... Ciao

si però questo metodo non funziona se la variabile contiene un numero con più di 2 decimali!

si però questo metodo non funziona se la variabile contiene un numero con più di 2 decimali!

Perchè? Puoi moltiplicare per 1000 o anche per 10000. Questo metodo funziona fino a che ottieni un numero unsigned int, e cioè fino a 65535, in compenso è facile e veloce

si però devi ogni volta modificare il moltiplicatore in base al numero di decimali.. pensa se devi convertire un float che una volta a 1 decimale, una volta 3, una volta 2 ecc.. ti complichi la vita per niente...molto meglio usare le unioni :) :)

Ciao,
Con una union non ottieni la parte intera e decimale i byte della rappresentazione floating-point IEEE 754 del numero.
E’ comunque un buon modo per memorizzare sulla memoria i dati.

flz47655: Con una union non ottieni la parte intera e decimale i byte della rappresentazione floating-point IEEE 754 del numero.

Ma la domanda non era come separare le due parti, era come memorizzare un float, ma vale anche per un int o long int, su EEPROM, e per questa cosa l'unione è il modo migliore, ed anche quello più efficiente dal punto di vista cicli macchina, per procedere.

Mi sono fatto ingannare dal titolo del thread, in effetti ho letto tutto molto di fretta saltando pezzi qua e la :sweat_smile:

Non vorrei riesumare un vecchio topic ma sono agli inizi e piuttosto che aprire un nuovo topic ho pensato di cercare con attenzione tra le varie dicussioni.
Io devo salvare in EEPROM un float, per ora con gli interi mi viene tutto bene.
Ho provato questo metodo

ma quando vado a lggere cosa c’è in A tramite un Serial.print mi ritorna 10.00 anziche, credo, 10.75 e non capisco come mai!

è un trucco dell'autocast. ti spiego:

float a = b/100;

cosa c'è di strano? che che b è in, e anche 100 è int. quindi il compilatore dà per contato che anche ilrisulato si un int (quindi troca la parte decimale). poi trasfoma in float e assegna ad a.

quindi, visto che l'autocast usa la variabile "più grande" che trova in una operazione (ed esegue un autoscat per le altre), basta forzare b o 100 ad essere float (per esempio 100.0 dovrebbe funzionare)

Federico0: [quote author=Dario Carlo link=topic=77970.msg589855#msg589855 date=1320624229] ho da poco affrontato il tuo stesso problema e l'ho risolto così: ho trasformato il float in unsigned int

float a = 10,75;
unsigned int b = a*100;

Ma questo è sbagliato. Il separatore decimale in C come in tutti i linguaggi di programmazione è il PUNTO, non la virgola. float a = 10,75 non va bene. Deve essere float a = 10.75.

Per tutto il resto vale il discorso di lesto.

lesto: è un trucco dell'autocast. ti spiego:

float a = b/100;

cosa c'è di strano? che che b è in, e anche 100 è int. quindi il compilatore dà per contato che anche ilrisulato si un int (quindi troca la parte decimale). poi trasfoma in float e assegna ad a.

quindi, visto che l'autocast usa la variabile "più grande" che trova in una operazione (ed esegue un autoscat per le altre), basta forzare b o 100 ad essere float (per esempio 100.0 dovrebbe funzionare)

@Lesto: sono un pollo e devo mparare a leggere due volte quello che mi si dice, ti ringrazo davvero! Ho capito come ha ragionato il compilatore e quindi per forza mettendogli il .0 funziona, grazie mille!

@leo72: ti ringrazio per la risposta ma non è la , o il . in problema, sapevo che serve il puntino, il problema penso sia tutt'altro