I was writing a piece of C/C++ code on custom hardware, but it did not have the ability to print floats using %f
, something due to its unsupport for uart ftdi.
So, I wrote code that prints a float value using %d
and also its buffer memory by using pointers.
Code:
#include <stdio.h>
int main()
{
float a = 6.7;
printf("float a = %f\n", a);
unsigned int a_int = *(int*)&a;
printf("intbufa = %d\n", a_int);
printf("hex a = 0x%x\n", a);
printf("int a = %d\n", a);
int comp_1s = ~a_int;
printf("comp_1s = %d\n", comp_1s);
int comp_2s = ~a_int + 1;
printf("comp_2s = %d\n", comp_2s);
}
The output looks like
float a = 6.700000
intbufa = 1087792742
hex a = 0xc0000000
int a = -1073741824
comp_1s = -1087792743
comp_2s = -1087792742
What I'm not understanding is the result of printing int a
. I recall it has something to do with 2's complement.
Can anyone explain it?
The output from printf("int a = %d\n", a);
is meaningless with regard to float
and the encoding of float
.
Since %d
expects an int
and a
is a float
, the behavior is not defined by the C standard. A common behavior in modern systems is for the float
to be passed in a floating-point register but for the printf
to take the value for %d
from an integer register. The result of this is that the value printf
prints has nothing to do with the float
value or its encoding.
In fact, the value printed, −1073741824, is C000000016, so we can see it is not related to the floating-point encoding of 6.7, which is 40D6666616 in the format commonly used for float
. It was just some other value left in a processor register, likely by the prior printf
. The same is true of the hex a
value.
unsigned int a_int = *(int*)&a;
is not a proper way to get the encoding of a float
because it violates the C standard’s rules for aliasing types. You can get the encoding in a defined way using a union or memcpy
, shown below.
You can print the encoding of a float
in a way defined by the C standard by using a union:
union { float f; unsigned u; } x = {a};
printf("0x%08X\n", x.u);
or copying the bytes:
unsigned u;
memcpy(&u, &a, sizeof u);
printf("0x%08X\n", u);
or by printing the individual bytes:
printf("0x");
for (size_t i = 0; i < sizeof a; ++i)
printf("%02x", ((unsigned char *) &a)[i]);
printf("\n");
The first two of these require that float
and unsigned
have the same size, which is common in current C implementations. They will usually print the bytes in the same order as humans write numbers, from most significant to least. (They will do this as long as float
and unsigned
have the same byte ordering.)
The last does not depend on the size of unsigned
but prints the bytes in memory order, not necessarily from most significant to least.