I need to write an IEEE single-precision floating point number to a 32-bit hardware register at a particular address. To do that, I need to convert a variable of type float to an unsigned integer. I can get the integer representation like this:
float a = 2.39;
unsigned int *target;
printf("a = %f\n",a);
target = &a;
printf("target = %08X\n",*target);
which returns:
a = 2.390000
target = 4018F5C3
All good. However this causes a compiler warning "cast.c:12: warning: assignment from incompatible pointer type"
Is there any other way to do this which doesn't generate the warning? This is for specific hardware, I don't need to handle different endianness etc and I don't want to loop through each char for performance reasons as some other questions tend to suggest. It seems like you might be able to use reinterpret_cast in C++ but I am using C.
You can use type punning with a union,
union {
float f;
uint32_t u;
} un;
un.f = your_float;
uint32_t target = un.u;
to get the bits. Or you can use memcpy
.
You could creat a union
type that contains a float and an unsigned int, store a value into the float member, then read it out of the int, like so:
union reg_val
{
float f_val;
unsigned int i_val;
} myRegister;
myRegister.f_val = 2.39;
printf("target = %08X", myRegister.i_val);
If you're trying to simply display the integral value of the float
as it's stored in memory, then try using a union:
union {
float a;
unsigned int target;
} u;
Store the float value:
u.a = 2.39;
Print both float and integer values:
printf ("a = %f\n", u.a);
printf ("target = %08X\n", u.target); /* you could use %u to show decimal */
No compiler warnings. I use GNU compiler (gcc) on Linux.
Notice that target
is not a pointer; this is the beauty (and hideousness) of unions. ;-)
EDIT: The union solution works everywhere I have tried it but somewhere on SO I had been pointed at standards that showed it didnt have to work. See the link below in the comments to find a LOT more info on this (Thank you Daniel!). Supposed to work or not supposed to work I would use it with care, I imagine endianness, etc gets involved as well (doubles broken into bytes, etc).
Another solution is a dummy asm function. For example on arm:
.globl echo
echo:
bx lr
unsigned int echo ( float );
...
unsigned int ra; float f;
f=1.0;
ra=echo(f);
some disassembly is required, needs to be on a system that doesnt have an fpu and/or uses gprs for carrying around floats.
memcpy as already mentioned is the cleanest and most reliable and portable solution (be aware of endianness).