views:

859

answers:

5

How do I take four received data bytes and assemble them into a floating-point number?

Right now I have the bytes stored in an array, which would be, received_data[1] ... received_data[4]. I would like to store these four bytes as a single 32-bit single precision float.

-Thanks

I'm actually receiving a packet with 19 bytes in it and assembling two sets of four bytes to make two floating-point numbers. So received_data[1] to received_data[4] is one float, and received_data[5] to received_data[8] is the other...

*UPDATE:** More Info...
The first bit, or the sign bit of the float, is the seventh bit the the first byte, here's what I did to check...

#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))  


if( CHECK_BIT(received_data[1], 7))  //set or clear LED2
          {LATAbits.LATA2=1;}else{LATAbits.LATA2=0;}  
if( CHECK_BIT(received_data[5], 7))  //set or clear LED3
          {LATAbits.LATA3=1;}else{LATAbits.LATA3=0;}

I then checked this by sending alternating positive and negative numbers from my data source, which should have changed the LED's accordingly and it worked.

As long as all that holds true, that means the bytes are in the right order, but they are stored little-endian, correct? Will this be stored into the float in the right order? I haven't had any luck parsing the bytes and reading it as a float yet...

If anyone has any experience with it, I'm using the C18 MPLAB IDE compiler.

FINAL UPDATE:
I have my application running! Just some minor bug fixes now!

THANKS!

Thanks for all the help guys! I'm new to stackoverflow, but this community on here is truly awesome! And a tool like this is truly priceless! I can not even begin to tell you how much time and frustration you have all saved me!

A: 

Assuming they're of the right format already:

float f = *((float*)(&received_data[1]));

Make sure you don't mean received_data[0]. I've left it as one in case you have a filler byte.

paxdiablo
that breaks the C aliasing rule, may not work with all compiles and may later turn into a very hard to find bug.
Nils Pipenbrinck
The fact that we're dealing with a type not existent most systems probably throws the portability concerns out the window anyway.
Dana the Sane
Strict aliasing was added by ISO as a crutch for people who don't know what they're doing, IMNSHO. I see no reason to waste CPU cycles doing a memcpy() that has exactly the same problems aliasing is supposed to prevent (*possible* incompatible types).
paxdiablo
I'd prefer to run with no-strict-aliasing. If I wanted to be treated like a baby, I'd be coding in VB :-) [apologies to all those VB coders out there].
paxdiablo
A: 

How about just copying the data via memcpy?

inline float FloatFromByteArray (const unsigned char * received_data)
{
  float f;
  memcpy (&f, received_data, sizeof (float));
  return f;
}

It copies the data starting at received_data[0]. If you want to copy from received_data[1] just add the offset inside memcpy.

inline float FloatFromByteArray (const unsigned char * received_data)
{
  float f;
  memcpy (&f, received_data+1, sizeof (float));
  return f;
}
Nils Pipenbrinck
A: 

You could assemble them in an order that depends on the endianness of your machine, then cast to a float*, and use that pointer to access them.

You would usually but the also put them in data[0]...data[3]. So...

char *data;

//Assemble in the *right* order
data[0] = received_data[4]; // index is machine dependant!
...

// cast
float *fptr = (float *)data;
printf ("%f\n",*fptr);


From your edit, perhap you should just dump this stuff into a struct:

struct packet_s {
   char byte0;
   float f1;
   float f2;
   ...
}

Then just read (2) or whatever directly into the struct. This method has been discussed on SO before. For instance:

dmckee
Will this work? I thought that a consistent set of higher or lower end bits were used for the mantissa, etc. Won't this result in the data not being aligned correctly?
Dana the Sane
@Dana: Which one? The struct dump method does makes some assumptions about endianness...The bit up top allows you to fix that.
dmckee
Nevermind, I forgot that floats are actually 32 bits rather than something bigger :-\
Dana the Sane
+2  A: 

You need to be sure the byte order is correct. Either

float f1 = *(float*)(received_data+1)
float f2 = *(float*)(received_data+5)

or if you have to reverse the bytes, you'll have to copy, so here's an alternative:

union {
  char chars[4];
  float f;
} u;

for (i = 0; i < 4; i++)
  u.chars[3-i] = received_data[i+1];
float f1 = u.f;     
// ... and similarly for second float

It had better be the case that these bytes were written by something like the inverse of this process (either you took the address of a float and cast it to char * or you had a union of char[4] and `float'.)

Norman Ramsey
union is nice here. Wish I'd remembered that...
dmckee
Verify the union works on your platform, of course. This use is Implementation Defined Behavior, I believe, meaning it is allowed to not work on some implementations, but they have to say so. If it works, it is almost certainly the right approach because it allows for easy handling of the byte order, and discourages use of a struct directly on the wire. If you go the pointer casting route, you might need to verify that the misaligned pointer is allowed by your CPU.
RBerteig
@RBerteig: I'm curious about why it is implementation defined. Can you please explain?
dreamlax
A: 

This is how I buffer a float:

static char* bufferFloat    (int* bLen, float value) {
*bLen = sizeof(float);
char* buf = malloc (*bLen);
int storage;

memcpy (&storage, &value, sizeof (int));
uint32_t val = htonl (storage);
memcpy (buf, &val, *bLen);

return buf;
}

This is how I receive a float:

static float readFloat (char* buf, int* bLen) {
float ret;
int temp;

if (*bLen < sizeof (float)) {
 *bLen = 0;
 return 0.0f;
}

memcpy (&temp, buf, sizeof (int));
uint32_t val = htonl(temp);
memcpy (&ret, &val, sizeof (float));
*bLen = sizeof (float);

return ret;
}

This is apart of some hobby networking code i wrote. I hope it helps and it seems to work perfectly for me. :D

Actually, if you want that networking code go here: http://cgi.cse.unsw.edu.au/~robertm/myProjects.php and find the right project.

Robert Massaioli
I guess it works, but using memcpy() for a 4-byte value seems like massive overhead. As does malloc():ing the same amount, you might want to rethink your buffer management.
unwind
Yeah, my approach might make more sense if you look at the link i provided.
Robert Massaioli