Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reassemble float from bytes inline

I'm working with HiTech PICC32 on the PIC32MX series of microprocessors, but I think this question is general enough for anyone knowledgable in C. (This is almost equivalent to C90, with sizeof(int) = sizeof(long) = sizeof(float) = 4.)

Let's say I read a 4-byte word of data that represents a float. I can quickly convert it to its actual float value with:

#define FLOAT_FROM_WORD(WORD_VALUE) (*((float*) &(WORD_VALUE)))

But this only works for lvalues. I can't, for example, use this on a function return value like:

FLOAT_FROM_WORD(eeprom_read_word(addr));

Is there a short and sweet way to do this inline, i.e. without a function call or temp variable? To be honest, there's no HUGE reason for me to avoid a function call or extra var, but it's bugging me. There must be a way I'm missing.

Added: I didn't realise that WORD was actually a common typedef. I've changed the name of the macro argument to avoid confusion.

like image 878
detly Avatar asked Feb 10 '10 05:02

detly


3 Answers

You can run the trick the other way for return values

float fl;
*(int*)&fl = eeprom_read_word(addr);

or

#define WORD_TO_FLOAT(f)  (*(int*)&(f))

WORD_TO_FLOAT(fl) = eeprom_read_word(addr);

or as R Samuel Klatchko suggests

#define  ASTYPE(type, val) (*(type*)&(val))
ASTYPE(WORD,fl) = eeprom_read_word(addr);
like image 164
John Knoeller Avatar answered Nov 05 '22 16:11

John Knoeller


If this were GCC, you could do this:

#define atob(original, newtype) \
  (((union { typeof(original) i; newtype j })(original)).k)

Wow. Hideous. But the usage is nice:

int i = 0xdeadbeef;
float f = atob(i, float);

I bet your compiler doesn't support either the typeof operator nor the union casting that GCC does, since neither are standard behavior, but in the off-chance that your compiler can do union casting, that is your answer. Modified not to use typeof:

#define atob(original, origtype newtype) \
  (((union { origtype i; newtype j })(original)).k)

int i = 0xdeadbeef;
float f = atob(i, int, float);

Of course, this ignores the issue of what happens when you use two types of different sizes, but is closer to "what you want," i.e. a simple macro filter that returns a value, instead of taking an extra parameter. The extra parameters this version takes are just for generality.

If your compiler doesn't support union casting, which is a neat but non-portable trick, then there is no way to do this the "way you want it," and the other answers have already got it.

like image 37
Chris Lutz Avatar answered Nov 05 '22 18:11

Chris Lutz


you can take the address of a temporary value if you use a const reference:

FLOAT_FROM_WORD(w) (*(float*)&(const WORD &)(w))

but that won't work in c :(

(c doesn't have references right? works in visual c++)

as others have said, be it an inlined function or a temp in a define, the compiler will optimize it out.

like image 1
matt Avatar answered Nov 05 '22 17:11

matt