hur sparas floats i AVR uC?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Isoz
Inlägg: 37
Blev medlem: 22 september 2004, 18:45:54

hur sparas floats i AVR uC?

Inlägg av Isoz »

jag har ett litet program som tar emot en float via UART'en på en AVR (dvs 4bytes som totalt skall ge en float, MSBytes skickas först)

frågan är nu
1 hur gör jag lämpligast om bytes'en -> en korrekt float (sitter och härjar med GCC...)

Mvh
Oscar Isoz
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

Vet inte, men tror att om GCC inte går ifrån C-standarden när den gör AVR-program ska de ju sparas på samma sätt som i PC. Då borde du t.ex. kunna skicka och ta emot med hjälp av pekare.

Kod: Markera allt

//PC:
float f;
char *fP;
fP=&f;

for (int i=0;i<sizeof(f);i++)
putchar(fP[i]);

//AVR;
float f;
char *fP;
fP=&f;

for (int i=0;i<sizeof(f);i++)
fP[i]=getchar();
Såg nu att du skrivit att MSB skickas först, isf borde det bara att vända på loopen i AVR-koden.
Isoz
Inlägg: 37
Blev medlem: 22 september 2004, 18:45:54

Inlägg av Isoz »

Tack för hjälpen!

men... jag fick det inte att funka direkt där jag skulle använda det så jag satte mig och började ifrån "början"....

Nu ser koden ut så här (det den gör är att den kopierar en float till en annan genom att flytta byte för byte).

ch2_ptr = &f2;
c_ptr = &f;
f = M_PI;

for(int i= 0;i <= sizeof(f);i++){
ch2_ptr = c_ptr;
}

får ut nästan rätt på displayen

f = 3.141560316085
f2 =3.141540527343

Men det jag inte begriper är hur sjutton kan dom vara olika om det är samma bitmönster (viket det är...eller??....)??

/Isoz
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Hur får du ut det där på "displayen" ??

Kom också ihåg att en float (nästan) alltid är ett ungefärligt värde
(med mindre eller större noggranhet)...
Användarvisningsbild
Andax
Inlägg: 4379
Blev medlem: 4 juli 2005, 23:27:38
Ort: Jönköping

Inlägg av Andax »

Skumt att det skrivs ut två olika värden trots att den ena är en kopia av den andra.

Om GCC följer IEEE 754 standarden för flyttal kan du hitta mer info om hur talen representeras t.ex. här

Med en float har man ca 6-7 siffrors noggrannhet och med double får man ca 15-16 siffrors noggrannhet.
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

Jag vet inte om det här är felet, men jag är rätt säker på att raden:

for(int i= 0;i <= sizeof(f);i++)

inte ska innehålla "<=" utan "<". Då kommer den loopa rätt antal gånger.

Sen behövs det inte en "int" som index (som jag skrev) utan en "char" borde räcka, och dessutom göra progammet snabbare.
(Tror jag, beror på om indirekt adressering använder 8- eller 16-bitars variabler på AVR.)

Alltså, testa:
for(char i=0;i < sizeof(f);i++)
Isoz
Inlägg: 37
Blev medlem: 22 september 2004, 18:45:54

Inlägg av Isoz »

Sodjan: jag använder dtostrf för att konvertera mellan float ->char
bearing: '<' istället för '<=' ökar felet....

hmm detta var intressant har skrivit in följande kod:

char *ch_ptr;
char *ch2_ptr;
char *c_ptr;
char *c1_ptr;
char c_arr2[14];
char c_arr[14];
float f;
float f2;
float f3;

ch2_ptr = &f2;
ch_ptr = &f;
f = M_PI; //spara den fördefinerade PI i f (finns i math.h)
for(int i= 0;i <= (sizeof(f));i++){
ch2_ptr = ch_ptr;
}

xmitByte(0x90,INSTR); //byter till rad 2 på displayen
xmitString("f"); //Skriver ut 'f' på displayen
c1_ptr= dtostrf(f,11,12,c_arr); //Skriver ut floaten med 11 decimaler
xmitString(c1_ptr); //

f2 = M_PI; //Sparar M_PI i f2 (dvs exakt samma som görs i början av kodexemplet
xmitByte(0xA0,INSTR); // byter rad
xmitString("Pi"); //Skriver ut 'f' på displayen

c1_ptr= dtostrf(f2,11,12,c_arr); //Skriver ut f2 på displayen
xmitString(c1_ptr);

f3 = f-f2; //Differansen medllan f och f2
xmitByte(0x80,INSTR); //Skriv ut den överst på displayen
c1_ptr= dtostrf(f3,11,12,c_arr);
xmitString(c1_ptr);

Man kan tro att denna kod skulle generera följande på displayen
0.0000000000
f3.141592653589 (dom första 14 tecknen i definitionen på pi i math.h
Pi3.141592653589
men icke :( utan istället får jag

0.000024318695
f3.141564846038
Pi3.141592741012

hmm kompilatorn verkar ändra min float på något mystiskt sätt....

hittade dock http://babbage.cs.qc.edu/IEEE-754/Decimal.html där man kan omvandla mellan decimal och float -> (antar att GCC följer IEEE standarden...) om man avrundar defintionen på Pi så får man dom första 7 decimalerna som skrivs ut efter "Pi"

Frågan är dock vad tusan händer med floaten 'f' under det att 'for' satsen körs....Kan det vara något med optimeringen i kompilatorn?

Mvh
en konfunderad Isoz
ie
EF Sponsor
Inlägg: 1381
Blev medlem: 23 oktober 2006, 13:12:57
Ort: Tyresö

Inlägg av ie »

Hej.

Som tidigare sagts är i <= (sizeof(f) fel, det ska vara < bara. Tänk att storleken på f är 1. När du går in i for-satsen sätts i till 0, testas 0<1, loopen körs ett varv, i ökas till 1, testas igen 1<1 är fel varvid for-satsen avslutats. Du har alltså genomlöpt loopen 1 gång och det var väl det du ville. Med <= hade det blivit två gånger.

Sen förstår jag inte varför du kör loopen alls eftersom du skriver över värdet av f2 senare.

Nu känner jag inte till dtostrf, men vad jag hittade på nätet så definieras den som char* dtostrf(double __val, char __width, char __prec, char * __s ). Det går alltså inte med float. Dessutom kommer sedan width och därefter antalet decimaler. Du vill alltså ha 12 decimaler i ett 11-teckensfält.

Huvudskälet till felet är antagligen att du använder float i st f double.
Användarvisningsbild
Andax
Inlägg: 4379
Blev medlem: 4 juli 2005, 23:27:38
Ort: Jönköping

Inlägg av Andax »

ie, sizeof(f) borde blir 4 och inte 1 eftersom det är en float.
ie
EF Sponsor
Inlägg: 1381
Blev medlem: 23 oktober 2006, 13:12:57
Ort: Tyresö

Inlägg av ie »

Jag vet. Jag gjoder bara antagandet att det var ett för att lättare se hur många gånger loopen genomlöptes. Det är lättare att testa på ytterligheter.
Användarvisningsbild
Stinrew
Inlägg: 954
Blev medlem: 20 augusti 2006, 03:14:41
Ort: Motala
Kontakt:

Inlägg av Stinrew »

Ja du Isoz, vad du krånglar till det. Du borde frågat mig eller PaGGe nere på XP-el. Det är precis som ie säger, M_PI är en 64b double som du läser in till ett 32b float. En 64b double består ju av 52b mantissa och 11b exponent, medans en 32b float består av 23b mantissa och 8b exponent. Hur denna typkonvertering trunkeras vet jag inte, men det är givet att det är detta fenomen som ger dina avvikelser på PIs decimaler. För att återgå till ditt egentliga problem föreslår jag att du skippar alla försök med pekare och använder dig av union istället. Jag ska försöka illustrera hur:

#include <stdio.h>

typedef union {
float f;
unsigned char auc[4];
} CHAR2FLOAT;

void main() {

CHAR2FLOAT longitud, latitud;
for(unsigned char ucI=0;ucI<4;ucI++)
longitud.auc[ucI] = läs in RX_UART;
longitud.f är nu en float som har värdet som din GPS-modul just överfört
}
Skriv svar