Search code examples
c++std-bitset

Why does std::bitset only support integral data types? Why is float not supported?


On trying to generate the bit pattern of a float as follows:

std::cout << std::bitset<32>(32.5) << std::endl;

the compiler generates this warning:

warning: implicit conversion from 'double' to 'unsigned long long' changes value
  from 32.5 to 32 [-Wliteral-conversion]
 std::cout << std::bitset<32>(32.5) << std::endl;

Output on ignoring warning :) :

00000000000000000000000000100000

Why cannot bitset detect floats and correctly output bit sequence, when casting to char* and walking memory does show correct sequence? This works, but is machine dependent on byte ordering and mostly unreadable:

template <typename T>
  void printMemory(const T& data) {
    const char* begin = reinterpret_cast<const char*>(&data);
    const char* end = begin + sizeof(data);
    while(begin != end)
      std::cout << std::bitset<CHAR_BIT>(*begin++) << " ";
    std::cout << std::endl;
}

Output:

00000000 00000000 00000010 01000010 

Is there a reason not to support floats? Is there an alternative for floats?


Solution

  • With all the usual warnings about floating point formats not being standardised, endianness, etc etc

    Here is code that will probably work, at least on x86 hardware.

    #include <bitset>
    #include <iostream>
    #include <type_traits>
    #include <cstring>
    
    constexpr std::uint32_t float_to_bits(float in)
    {
        std::uint32_t result = 0;
        static_assert(sizeof(float) == sizeof(result), "float is not 32 bits");
        constexpr auto size = sizeof(float);
        std::uint8_t buffer[size] = {};
        // note - memcpy through a byte buffer to satisfy the
        // strict aliasing rule.
        // note that this has no detrimental effect on performance
        // since memcpy is 'magic'
        std::memcpy(buffer, std::addressof(in), size);
        std::memcpy(std::addressof(result), buffer, size);
        return result;
    }
    
    constexpr std::uint64_t float_to_bits(double in)
    {
        std::uint64_t result = 0;
        static_assert(sizeof(double) == sizeof(result), "double is not 64 bits");
        constexpr auto size = sizeof(double);
        std::uint8_t buffer[size] = {};
        std::memcpy(buffer, std::addressof(in), size);
        std::memcpy(std::addressof(result), buffer, size);
        return result;
    }
    
    
    int main()
    {
        std::cout << std::bitset<32>(float_to_bits(float(32.5))) << std::endl;
        std::cout << std::bitset<64>(float_to_bits(32.5)) << std::endl;
    }
    

    example output:

    01000010000000100000000000000000
    0100000001000000010000000000000000000000000000000000000000000000