3

Can you have bitset container of floating data types? Example:

bitset<sizeof(float)*sizeof(char)> second(5.5f);
cout << second.to_string() << endl;

It doesn't work correctly. what i'm trying to do is get the bit representation.

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
user942451
  • 1,595
  • 2
  • 12
  • 6
  • 1
    `sizeof(char)` is pointless. The size of a `char` is, by *definition*, one. – John Dibling Sep 23 '11 at 19:24
  • @John - He meant to say `sizeof(float)*CHAR_BIT`. (See [here](http://stackoverflow.com/questions/2247188/get-number-of-bits-in-char)). – Robᵩ Sep 23 '11 at 19:31
  • Please tell us why on Earth you are trying to do this? Guess curiousity might kill this Cat! – Ed Heal Sep 23 '11 at 19:34
  • @user: Why do you want to do this? If you are just trying to hack something together, use `reinterpret_cast`. If you need a solution that will work reliably, then we need to know what you're trying to accomplish. – John Dibling Sep 23 '11 at 19:36
  • I'm only trying to check which bits are set inside float type, which also seem illegal using bitwise operators e.g `2.5f & 2` and bitset container, is there any way around using bitwise? – user942451 Sep 23 '11 at 19:48
  • Yes, but why do you want to check which bits are set within a float? – John Dibling Sep 23 '11 at 20:07
  • 1
    Because i've done with a byte, int, long, so i wanted to see whats in float? is there something special about floating types that i'm not aware of? – user942451 Sep 23 '11 at 20:11

2 Answers2

9

bitset only takes a unsigned long as its constructor argument. In your example the float is converted to unsigned long and then used as the argument.

To get what you desire use something along the lines of:

float f = 5.5f;
std::bitset<sizeof(float)*CHAR_BIT> foo(*reinterpret_cast<unsigned long*>(&f));

This reinterprets the "memory" the float is in as memory containing an unsigned long and thus "tricks" the constructor.

pmr
  • 58,701
  • 10
  • 113
  • 156
  • @Mooing Duck Thanks, those comments were made after my answer. – pmr Sep 23 '11 at 20:01
  • 2
    [This is UB](http://eel.is/c++draft/basic.lval#11). You should `memcpy` the float to an integer instead of reinterpret it. – YSC Jan 19 '18 at 15:15
2

In order to build a bitset from a float in a well-defined manner, you should:

  1. ensure your types have compatible sizes;
  2. copy the representation of your float into a compatible integer;
  3. build a bitset from that integer.

Here is a minimal example:

#include <bitset>
#include <cstring>

float f = 1.618;
static_assert(sizeof(float) <= sizeof(long long unsigned int), "wrong sizes"); // 1.
long long unsigned int f_as_int = 0;
std::memcpy(&f_as_int, &f, sizeof(float)); // 2.
std::bitset<8*sizeof(float)> f_as_bitset{f_as_int}; // 3.

But, this won't work as expected on a big endian target whose long long unsigned int is larger than float, because the bitset constructor always pick the least significant bits of the argument, regardless of the endianness. And this sucks.

So, if we want to be complete, we need to address this. To do so, one can copy the float to an integer of the same size (2.), then cast (3.) it to a long long unsigned int, resulting to a endianness-agnostic representation. But yeah, an endianness-agnostic solution is tedious:

#include <bitset>
#include <cstring>
#include <cstdint>
#include <iostream>

namespace details
{
    template<unsigned nbits> struct uint {};
    template<> struct uint<8>  { using type = uint8_t; };
    template<> struct uint<16> { using type = uint16_t; };
    template<> struct uint<32> { using type = uint32_t; };
    template<> struct uint<64> { using type = uint64_t; };
}

template<class T>
using unsigned_integer_of_same_size = typename details::uint<sizeof(T)*8>::type;

int main()
{
    float f = -1./0.;
    static_assert(sizeof(float) <= sizeof(long long unsigned int), "wrong sizes"); // 1.
    unsigned_integer_of_same_size<float> f_as_uint = 0;
    std::memcpy(&f_as_uint, &f, sizeof(float)); // 2.
    std::bitset<8*sizeof(float)> f_as_bitset{f_as_uint}; // 3. && 4.

    std::cout << f_as_bitset << "\n";
}

(live demo)

YSC
  • 38,212
  • 9
  • 96
  • 149
  • On a big endian system where `long long unsigned int` is larger than `float` it won't work right because it'll copy the float bits into the bits of the integer that are thrown away when constructing the bitset. – Mark B Jan 19 '18 at 16:13
  • @MarkB That's true, the bitset constructor always pick the least significant bits of the argument, regardless of the endianness of the target. I didn't know. What would you suggest? – YSC Jan 19 '18 at 16:24
  • since you already know the exact size of float, isn't it easier to cast the float into a uint type? something like `using int_type = unsigned_integer_of_same_size; std::bitset<8*sizeof(float)> f_as_bitset{*(int_type*)(&f)};` – qed Nov 09 '18 at 09:38
  • @qed As commented under the accepted answer, this is undefined behavior (as per [`[basic.lval]/11`](http://eel.is/c++draft/basic.lval#11)). But don't worry, the compiler is smart and knows how to avoid unnecessary copies ;) – YSC Nov 09 '18 at 09:41