how to print float as hex bytes format in C?
Solution 1
What you are missing is how to obtain a valid look at the bits that make up the IEEE-754 single-precision floating point number as its equivalent unsigned int
representation in memory. To do that while avoiding violating the strict aliasing rule (type punning pointers), you can use a union
between float
and unsigned
(or better uint32_t
for exact width type), e.g.
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
int main (void) {
float pi = (float)M_PI;
union {
float f;
uint32_t u;
} f2u = { .f = pi };
printf ("pi : %f\n : 0x%" PRIx32 "\n", pi, f2u.u);
return 0;
}
Using unsigned
instead of uint32_t
and understanding it may not be 32-bits on all hardware, the standard format string will suffice, e.g. printf ("pi : %f\n : 0x%0x\n", pi, f2u.u);
. (you can drop the include of stdint.h
and inttypes.h
in that case)
Example Use/Output
This will allow you to get the hex representation of the bits that make up the floating-point number when interpreted as an unsigned type:
$ ./bin/float_hex
pi : 3.141593
: 0x40490fdb
Solution 2
To be clear, OP's code did not fail due to "strict aliasing rule".
You can use char*
for aliasing instead of your system's word. The rules allow an exception for char*
Code failed as "%x"
expects an unsigned
and code passed a float
. This is undefined behavior (UB). As FP values are sometimes passed in a different manner than integers, this is not surprising.
float a = 3.141592654;
printf("test 1 : a = %x\n", a); // bad code
OP's later code was almost right. Just needs to use the size of the object and hh
in the specifier to only print the the value without its signed extension.
The order to print depends on endian-ness of the float
. Using a union still is not endian correct as endian of of a float
and an int
may differ - though that is uncommon.
char *pt = (char *)&a;
//for(i=0;i<4;i++){
for(i=0;i<sizeof a;i++){
//printf("%x ", (char)(*pt++));
printf("%02hhx ", *pt++);
}
Additional issues exist for rare platforms that have more than 8 bits to a char
.
The easy alternative is to use "%a"
@user2357112 which will print the mantissa/significand in hexadecimal and the exponent in decimal powers-of-2.
To covert any object to a "hex" string, this code serves as an example to convert any object into a "binary" string.
Solution 3
I think the answer I was looking for is like this.
float a = 3.141592654;
printf("test 1 2nd time : a = %x\n", *(unsigned int*)&a);
Then I get
test 1 2nd time : a = 40490fdb
Chan Kim
Hi folks, I used to be a hardware engineer, but I am working on S/W area these years. Mostly I'll get help from this site, but I hope someday I can give helps to others. Thank you! Chan Kim visit my home page : http://chankim.dothome.co.kr
Updated on June 19, 2022Comments
-
Chan Kim almost 2 years
I wanted to see the float value 3.14159265 in IEEE754 format representation. So wrote this test code.
#include <stdio.h> main() { int i; float a = 3.141592654; printf("a = %f\n", a); // print test 1 printf("test 1 : a = %x\n", a); printf("test 1 2nd time : a = %x\n", a); // print test 2 char *pt = (char *)&a; printf("test 2 :\n"); for(i=0;i<4;i++){ printf("%x ", (char)(*pt++)); } printf("\n"); }
When I run it, several times, it shows like this :
ckim@stph45:~/test5] test a = 3.141593 test 1 : a = e0cd2000 test 1 2nd time : a = e0cd2000 test 2 : ffffffdb f 49 40 ckim@stph45:~/test5] test a = 3.141593 test 1 : a = 520db000 test 1 2nd time : a = 520db000 test 2 : ffffffdb f 49 40 ckim@stph45:~/test5] test a = 3.141593 test 1 : a = 3b373000 test 1 2nd time : a = 3b373000 test 2 : ffffffdb f 49 40 ckim@stph45:~/test5] test a = 3.141593 test 1 : a = 18a6d000 test 1 2nd time : a = 18a6d000 test 2 : ffffffdb f 49 40
First question is : when using printf with %x, I expected to see 40490fdb which is 3.14159265 in IEEE754 format. But the value changes everytime I run it as you can see. What's wrong with this here? Maybe I'm missing something very basic.
Anoother question is, when I use character pointer and print the hex values as in test 2 above, how can I print 'db' instead of 'fffffdb'? (the value is shown sign extened). I think I've done these things before but can't remember it now. (My machine is little endian so LSB is shown first.)
-
Admin over 6 yearsFine, adding a "char pt[4];" within the union also solves the char issue.
-
David C. Rankin over 6 yearsVery true - you get to make up the 32-bits any way you like as long as you stay within the rules. Only issue with
char [4]
is you are then left to cast tounsigned
(oruint32_t
) at some later point (keyboard rant deleted):)
-
AnT stands with Russia over 6 yearsNo.
*(unsigned int*)&a
- that's a strict aliasing violation that is not guaranteed to work. The behavior is undefined. If you want to reinterpret memory - use a union as @David C. Rankin suggested. -
Chan Kim over 6 yearsThanks, using union is one method. I put my answer using
*(unsigned int*)&a
and that is the type of simple method I wanted. -
AnT stands with Russia over 6 years@Chan Kim: Your "simple method" is broken.
-
David C. Rankin over 6 years@ChanKim you are type punning a pointer which is not allowed in C (aside from
char
). Didn't you notice the warnings the compiler generated? You are violating Sections 6.5 (6) & (7) of the C11 Standard. -
Chan Kim over 6 yearsOk, though my compiler doesn't complain about usnig pointer type casting.. I learned this things are deemed bad pracice and we should use reinterpret cast. and learned about using union in this case. Thanks.
-
user2357112 over 6 years@ChanKim: There's no reinterpret_cast in C. C++ is a different language with different rules.
-
David C. Rankin over 6 years@chux
float a = 3.141592654; *(unsigned int*)&a
in the comment (6th above)