Search code examples
c++stdbinary-databitset

Showing binary representation of floating point types in C++


Consider the following code for integral types:

template <class T>
std::string as_binary_string( T value ) {
    return std::bitset<sizeof( T ) * 8>( value ).to_string();
}

int main() {
    unsigned char a(2);
    char          b(4);

    unsigned short c(2);
    short          d(4);

    unsigned int   e(2);
    int            f(4);

    unsigned long long g(2);
    long long h(4);

    std::cout << "a = " << +a << " " << as_binary_string( a ) << std::endl;
    std::cout << "b = " << +b << " " << as_binary_string( b ) << std::endl;
    std::cout << "c = " << c << " " << as_binary_string( c ) << std::endl;
    std::cout << "d = " << c << " " << as_binary_string( d ) << std::endl;
    std::cout << "e = " << e << " " << as_binary_string( e ) << std::endl;
    std::cout << "f = " << f << " " << as_binary_string( f ) << std::endl;
    std::cout << "g = " << g << " " << as_binary_string( g ) << std::endl;
    std::cout << "h = " << h << " " << as_binary_string( h ) << std::endl;

    std::cout << "\nPress any key and enter to quit.\n";
    char q;
    std::cin >> q;

    return 0;
}

Pretty straight forward, works well and is quite simple.


EDIT

How would one go about writing a function to extract the binary or bit pattern of arbitrary floating point types at compile time?


When it comes to floats I have not found anything similar in any existing libraries of my own knowledge. I've searched google for days looking for one, so then I resorted into trying to write my own function without any success. I no longer have the attempted code available since I've originally asked this question so I can not exactly show you all of the different attempts of implementations along with their compiler - build errors. I was interested in trying to generate the bit pattern for floats in a generic way during compile time and wanted to integrate that into my existing class that seamlessly does the same for any integral type. As for the floating types themselves, I have taken into consideration the different formats as well as architecture endian. For my general purposes the standard IEEE versions of the floating point types is all that I should need to be concerned with.

iBug had suggested for me to write my own function when I originally asked this question, while I was in the attempt of trying to do so. I understand binary numbers, memory sizes, and the mathematics, but when trying to put it all together with how floating point types are stored in memory with their different parts {sign bit, base & exp } is where I was having the most trouble.

Since then with the suggestions those who have given a great answer - example I was able to write a function that would fit nicely into my already existing class template and now it works for my intended purposes.


Solution

  • What about writing one by yourself?

    static_assert(sizeof(float) == sizeof(uint32_t));
    static_assert(sizeof(double) == sizeof(uint64_t));
    
    std::string as_binary_string( float value ) {
        std::uint32_t t;
        std::memcpy(&t, &value, sizeof(value));
        return std::bitset<sizeof(float) * 8>(t).to_string();
    }
    
    std::string as_binary_string( double value ) {
        std::uint64_t t;
        std::memcpy(&t, &value, sizeof(value));
        return std::bitset<sizeof(double) * 8>(t).to_string();
    }
    

    You may need to change the helper variable t in case the sizes for the floating point numbers are different.

    You can alternatively copy them bit-by-bit. This is slower but serves for arbitrarily any type.

    template <typename T>
    std::string as_binary_string( T value )
    {
        const std::size_t nbytes = sizeof(T), nbits = nbytes * CHAR_BIT;
        std::bitset<nbits> b;
        std::uint8_t buf[nbytes];
        std::memcpy(buf, &value, nbytes);
    
        for(int i = 0; i < nbytes; ++i)
        {
            std::uint8_t cur = buf[i];
            int offset = i * CHAR_BIT;
    
            for(int bit = 0; bit < CHAR_BIT; ++bit)
            {
                b[offset] = cur & 1;
                ++offset;   // Move to next bit in b
                cur >>= 1;  // Move to next bit in array
            }
        }
    
        return b.to_string();
    }