how to print float as hex bytes format in C?

13,992

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
Share:
13,992
Chan Kim
Author by

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, 2022

Comments

  • Chan Kim
    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
    Admin over 6 years
    Fine, adding a "char pt[4];" within the union also solves the char issue.
  • David C. Rankin
    David C. Rankin over 6 years
    Very 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 to unsigned (or uint32_t) at some later point (keyboard rant deleted) :)
  • AnT stands with Russia
    AnT stands with Russia over 6 years
    No. *(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
    Chan Kim over 6 years
    Thanks, 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
    AnT stands with Russia over 6 years
    @Chan Kim: Your "simple method" is broken.
  • David C. Rankin
    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
    Chan Kim over 6 years
    Ok, 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
    user2357112 over 6 years
    @ChanKim: There's no reinterpret_cast in C. C++ is a different language with different rules.
  • David C. Rankin
    David C. Rankin over 6 years
    @chux float a = 3.141592654; *(unsigned int*)&a in the comment (6th above)