#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2008
    Posts
    5
    Rep Power
    0

    Printing a float as a hex number


    hello all

    If a user enters a floatint point number like 10.1, I want to print out the hex number in floating point. The %X specifer allows for an int to be printed as a hex number and I heard you could do the samething with floating point numbers with casting and pointers but I can't seem to figure it out. Anyone have any ideas? Thanks
  2. #2
  3. Devshed God 1st Plane (5500 - 5999 posts)

    Join Date
    Jun 2005
    Posts
    5,964
    Rep Power
    4852
    Convert the whole number and the fraction to hex separately, then recombine.

    If you mean "express the binary pattern of the floating point representation in hex digits", that's a different story.
    Write no code whose complexity leaves you wondering what the hell you did.
    Politically Incorrect DaWei on Pointers Grumpy on Exceptions
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2008
    Posts
    5
    Rep Power
    0
    Originally Posted by sizablegrin
    Convert the whole number and the fraction to hex separately, then recombine.

    If you mean "express the binary pattern of the floating point representation in hex digits", that's a different story.
    I mean if 10.1 is entered the floating point Hex number would be 4121999A.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Feb 2004
    Location
    San Francisco Bay
    Posts
    1,939
    Rep Power
    1313
    So something like this?
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char **argv) {
        float a = 10.1;
    
        printf("%X\n", *(int *)&a);
    
        return 0;
    }

    Comments on this post

    • clifford agrees : I did not ignore your post, I just took several hours typing mine!
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2008
    Posts
    5
    Rep Power
    0
    Originally Posted by Lux Perpetua
    So something like this?
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char **argv) {
        float a = 10.1;
    
        printf("%X\n", *(int *)&a);
    
        return 0;
    }
    YES!!! Thank you that is what I was looking for! Thank you.

    Comments on this post

    • clifford agrees : It probably is, but beware of the circumstances in which it won't work (see below)
  10. #6
  11. Contributing User

    Join Date
    Aug 2003
    Location
    UK
    Posts
    5,109
    Rep Power
    1802
    If you simply cast a float to an integer type it performs a conversion that modifies the bit pattern, which defeats the purpose. So the trick is to take the address of the float, cast the pointer to an integer pointer (which has no effect on the bit pattern of the data), and then dereference it so that the data is interpreted as an integer but witout changing the bit pattern.

    You do have to be careful however to ensure that the floating point type and the integer type have the same bit-width. Doing that portably requires some thought, but basically, if we assume that both float and long are both the same width, which is generally true on the most commonly used platforms, then:

    Code:
    float f = 10.1f ;
    printf( "0x%8.8XL\n", *((long*)&f) ) ;
    However, to do that with a double, requires that your compiler support a 64bit integer type, which is not a given. Moreover printing a long double may prove difficult as there may not be an integer type of the same width.

    A more flexible method is to take the address of the FP value, cast it to a char* and iterate through it as a char array using the size of the FP type as a limit and outputting each character as a hex digit pair. This may display in a different byte order than the previous method depending on your processor, but you could choose to display from high to low address or vice versa. In the end it does not really matter, so long as you know what you are looking at. IEEE754 does not actually specify a byte order in any case.

    The char array method is an ideal application for a C++ template function so that it will handle any FP type. In fact it will allow you to display a hex dump of any type, including structures and classes.

    Example:
    C++ Code:
     
    #include <string>
    #include <sstream>
    #include <iomanip>
    #include <iostream>
     
    bool isLittleEndian()
    {
        static bool little_endian ;
        static bool endian_checked = false ;
        // runtime endianness check (once only)
        if( !endian_checked )
        {
            const short endian_test_pattern = 0x00ff ;
            little_endian = *(reinterpret_cast<const char*>(&endian_test_pattern)) == '\xff' ;
            endian_checked = true ;
        }
     
        return little_endian ;
    }
     
    template <class T> 
    std::string type_to_hex( const T arg )
    {
        std::ostringstream hexstr ;
        const char* addr = reinterpret_cast<const char*>(&arg) ;
     
        hexstr << "0x" ;
        hexstr << std::setw(2) << std::setprecision(2) << std::setfill('0') << std::hex ;
     
        if( isLittleEndian() )
        {
            for( int b = sizeof(arg) - 1; b >= 0; b-- )
            {
                hexstr << static_cast<unsigned>(*(addr+b) & 0xff) ;
            }
        }
        else
        {
            for( int b = 0; b < sizeof(arg); b++ )
            {
                hexstr << static_cast<unsigned>(*(addr+b) & 0xff) ;
            }
        }
        return hexstr.str() ;
    }
     
    using std::cout ;
    using std::endl ;
    int main()
    {
        const int i = 123 ;
        const float f = 0.123f ;
        const double d = 0.123 ;
        const long double ld = 0.123L ;
        const struct{ int x; int y; } s = { 123, 321 } ;
     
        cout << "i:  " << type_to_hex( i ) << endl ;
        cout << "f:  " << type_to_hex( f ) << endl ;
        cout << "d:  " << type_to_hex( d ) << endl ;
        cout << "ld: " << type_to_hex( ld ) << endl ;
        cout << "s:  " << type_to_hex( s ) << endl ;
     
        cout << "f using cast method: 0x" << std::hex << *((int*)&f) << endl ;
    }


    In Win32 using VC++ 2008 this outputs:

    Code:
    i:  0x00007b
    f:  0x3dfbe76d
    d:  0x3fbf7ced916872b0
    ld: 0x3fbf7ced916872b0
    s:  0x0001410007b
    f using cast method: 0x3dfbe76d
    Clifford

    Comments on this post

    • xlordt agrees : Well said.. I actually learned something new :P
    Last edited by clifford; November 1st, 2008 at 01:30 PM. Reason: endian check in a spearate function
  12. #7
  13. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2008
    Posts
    5
    Rep Power
    0
    Originally Posted by clifford
    If you simply cast a float to an integer type it performs a conversion that modifies the bit pattern, which defeats the purpose. So the trick is to take the address of the float, cast the pointer to an integer pointer (which has no effect on the bit pattern of the data), and then dereference it so that the data is interpreted as an integer but witout changing the bit pattern.

    You do have to be careful however to ensure that the floating point type and the integer type have the same bit-width. Doing that portably requires some thought, but basically, if we assume that both float and long are both the same width, which is generally true on the most commonly used platforms, then:

    Code:
    float f = 10.1f ;
    printf( "0x%8.8XL\n", *((long*)&f) ) ;
    However, to do that with a double, requires that your compiler support a 64bit integer type, which is not a given. Moreover printing a long double may prove difficult as there may not be an integer type of the same width.

    A more flexible method is to take the address of the FP value, cast it to a char* and iterate through it as a char array using the size of the FP type as a limit and outputting each character as a hex digit pair. This may display in a different byte order than the previous method depending on your processor, but you could choose to display from high to low address or vice versa. In the end it does not really matter, so long as you know what you are looking at. IEEE754 does not actually specify a byte order in any case.

    The char array method is an ideal application for a C++ template function so that it will handle any FP type. In fact it will allow you to display a hex dump of any type, including structures and classes.

    Example:
    C++ Code:
     
    #include <string>
    #include <sstream>
    #include <iomanip>
    #include <iostream>
     
    bool isLittleEndian()
    {
        static bool little_endian ;
        static bool endian_checked = false ;
        // runtime endianness check (once only)
        if( !endian_checked )
        {
            const short endian_test_pattern = 0x00ff ;
            little_endian = *(reinterpret_cast<const char*>(&endian_test_pattern)) == '\xff' ;
            endian_checked = true ;
        }
     
        return little_endian ;
    }
     
    template <class T> 
    std::string type_to_hex( const T arg )
    {
        std::ostringstream hexstr ;
        const char* addr = reinterpret_cast<const char*>(&arg) ;
     
        hexstr << "0x" ;
        hexstr << std::setw(2) << std::setprecision(2) << std::setfill('0') << std::hex ;
     
        if( isLittleEndian() )
        {
            for( int b = sizeof(arg) - 1; b >= 0; b-- )
            {
                hexstr << static_cast<unsigned>(*(addr+b) & 0xff) ;
            }
        }
        else
        {
            for( int b = 0; b < sizeof(arg); b++ )
            {
                hexstr << static_cast<unsigned>(*(addr+b) & 0xff) ;
            }
        }
        return hexstr.str() ;
    }
     
    using std::cout ;
    using std::endl ;
    int main()
    {
        const int i = 123 ;
        const float f = 0.123f ;
        const double d = 0.123 ;
        const long double ld = 0.123L ;
        const struct{ int x; int y; } s = { 123, 321 } ;
     
        cout << "i:  " << type_to_hex( i ) << endl ;
        cout << "f:  " << type_to_hex( f ) << endl ;
        cout << "d:  " << type_to_hex( d ) << endl ;
        cout << "ld: " << type_to_hex( ld ) << endl ;
        cout << "s:  " << type_to_hex( s ) << endl ;
     
        cout << "f using cast method: 0x" << std::hex << *((int*)&f) << endl ;
    }


    In Win32 using VC++ 2008 this outputs:

    Code:
    i:  0x00007b
    f:  0x3dfbe76d
    d:  0x3fbf7ced916872b0
    ld: 0x3fbf7ced916872b0
    s:  0x0001410007b
    f using cast method: 0x3dfbe76d
    Clifford
    Wow thank you clifford. I was wondering how the convertion worked. Thanks for the post you showed me some interesting things. Thanks again :thumbs:
  14. #8
  15. Contributing User

    Join Date
    Aug 2003
    Location
    UK
    Posts
    5,109
    Rep Power
    1802
    Originally Posted by kenshinofkin
    Wow thank you clifford.
    You are welcome, but there really s no need to re-quote the entire previous post! It is already there for all to see. Quoting is intended for making reference to out-of-order posts or particular points to make it clear what you are referring to. If you are merely referring to the previous post in its entirity, then no quote is needed.

    My scroll wheel hurts! ;)

IMN logo majestic logo threadwatch logo seochat tools logo