using a union is clean and easy
union
{
float f;
unsigned int ul;
unsigned char uc[4];
} myfloatun;
myfloatun.f=somenum;
printf("0x%08X\n",myfloatun.ul);
Much safer from a compiler perspective than pointers. Memcpy works just fine too.
EDIT
Okay, okay, here are fully functional examples. Yes, you have to use unions with care if you dont keep an eye on how this compiler allocates the union and pads or aligns it it can break and this is why some/many say it is dangerous to use unions in this way. Yet the alternatives are considered safe?
Doing some reading C++ has its own problems with unions and a union may very well just not work. If you really meant C++ and not C then this is probably bad. If you said kleenex and meant tissues then this might work.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef union
{
float f;
unsigned char uc[4];
} FUN;
void charRepresentation ( unsigned char *uc, float f)
{
FUN fun;
fun.f=f;
uc[0]=fun.uc[3];
uc[1]=fun.uc[2];
uc[2]=fun.uc[1];
uc[3]=fun.uc[0];
}
void floatRepresentation ( unsigned char *uc, float *f )
{
FUN fun;
fun.uc[3]=uc[0];
fun.uc[2]=uc[1];
fun.uc[1]=uc[2];
fun.uc[0]=uc[3];
*f=fun.f;
}
int main()
{
unsigned int ra;
float test;
char result[4];
FUN fun;
if(sizeof(fun)!=4)
{
printf("It aint gonna work!\n");
return(1);
}
test = 4.7567F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 1.0F;
charRepresentation(result,test);
for(ra=0;ra<;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 2.0F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 3.0F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 0.0F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 0.15625F;
charRepresentation(result,test);
for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
result[0]=0x3E;
result[1]=0xAA;
result[2]=0xAA;
result[3]=0xAB;
floatRepresentation(result,&test);
printf("%f\n",test);
return 0;
}
And the output looks like this
gcc fun.c -o fun
./fun
0x40 0x98 0x36 0xE3
0x3F 0x80 0x00 0x00
0x40 0x00 0x00 0x00
0x40 0x40 0x00 0x00
0x00 0x00 0x00 0x00
0x3E 0x20 0x00 0x00
0.333333
You can verify by hand, or look at this website as I took examples directly from it, the output matches what was expected.
http://en.wikipedia.org/wiki/Single_precision
What you do not ever want to do is point at memory with a pointer to look at it with a different type. I never understood why this practice is used so often, particularly with structs.
int broken_code ( void )
{
float test;
unsigned char *result
test = 4.567;
result=(unsigned char *)&test;
//do something with result here
test = 1.2345;
//do something with result here
return 0;
}
That code will work 99% of the time but not 100% of the time. It will fail when you least expect it and at the worst possible time, like the day after your most important customer receives it. Its the optimizer that eats your lunch with this coding style. Yes, I know most of you do this and were taught this and perhaps have never been burned....yet. That just makes it more painful when it finally does happen, because now you know that it can and has failed (with popular compilers like gcc, on common computers like a pc).
After seeing this fail when using this method for testing an fpu, programmatically building specific floating point numbers/patterns, I switched to the union approach which so far has never failed. By definition the elements in the union share the same chunk of storage, and the compiler and optimizer do not get confused about the two items in that shared chunk of storage being...in that same shared chunk of storage. With the above code you are relying on an assumption that there is non-register memory storage behind every use of the variables and that all variables are written back to that storage before the next line of code. Fine if you never optimize or if you use a debugger. The optimizer does not know in this case that result and test share the same chunk of memory, and that is the root of the problem/bug. To do the pointer game you have to get into putting volatile on everything, like a union you still have to know how the compiler aligns and pads, you still have to deal with endians.
The problem is generic that the compiler doesnt know the two items share the same memory space. For the specific trivial example above I have watched the compiler optimize out the assignment of the number to the floating point variable because that value/variable is never used. The address for the storage of that variable is used and if you were to say printf the *result data the compiler would not optimize out the result pointer and thus not optimize out the address to test and thus not optimize out the storage for test, but in this simple example it can and has happened where the numbers 4.567 and 1.2345 never make it into the compiled program. I have also see the compiler allocate the storage for test, but assign the numbers to a floating point register then never use that register nor copy the contents of that register to the storage that it has assigned. The reasons why it fails for less trivial examples can be harder to follow, often having to do with register allocation and eviction, change a line of code and it works, change another and it breaks.
Memcpy,
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void charRepresentation ( unsigned char *uc, float *f)
{
memcpy(uc,f,4);
}
void floatRepresentation ( unsigned char *uc, float *f )
{
memcpy(f,uc,4);
}
int main()
{
unsigned int ra;
float test;
unsigned char result[4];
ra=0;
if(sizeof(test)!=4) ra++;
if(sizeof(result)!=4) ra++;
if(ra)
{
printf("It aint gonna work\n");
return(1);
}
test = 4.7567F;
charRepresentation(result,&test);
printf("0x%02X ",(unsigned char)result[3]);
printf("0x%02X ",(unsigned char)result[2]);
printf("0x%02X ",(unsigned char)result[1]);
printf("0x%02X\n",(unsigned char)result[0]);
test = 0.15625F;
charRepresentation(result,&test);
printf("0x%02X ",(unsigned char)result[3]);
printf("0x%02X ",(unsigned char)result[2]);
printf("0x%02X ",(unsigned char)result[1]);
printf("0x%02X\n",(unsigned char)result[0]);
result[3]=0x3E;
result[2]=0xAA;
result[1]=0xAA;
result[0]=0xAB;
floatRepresentation(result,&test);
printf("%f\n",test);
return 0;
}
gcc fcopy.c -o fcopy
./fcopy
0x40 0x98 0x36 0xE3
0x3E 0x20 0x00 0x00
0.333333
With the flaming I am going to get about my above comments, and depending on which side of the argument you choose to be on. Perhaps memcpy is your safest route. You still have to know the compiler very well, and manage your endians. The compiler should not screw up the memcpy it should store the registers to memory before the call, and execute in order.