11

It's easy to construct a bitset<64> from a uint64_t:

uint64_t flags = ...;
std::bitset<64> bs{flags};

But is there a good way to construct a bitset<64 * N> from a uint64_t[N], such that flags[0] would refer to the lowest 64 bits?

uint64_t flags[3];
// ... some assignments
std::bitset<192> bs{flags};  // this very unhelpfully compiles
                             // yet is totally invalid

Or am I stuck having to call set() in a loop?

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 4
    Do you mean to take the lowest 8 bits from every `uint64_t`? And in which order? – Wintermute Apr 27 '15 at 17:25
  • @Wintermute Oops. I want all the bits. `flags[0]` would be the lowest 64 bits. – Barry Apr 27 '15 at 17:28
  • Maybe something like `constexpr std::size_t FLAGS_SIZE = 3; uint64_t flags[FLAGS_SIZE]; std::bitset<64 * FLAGS_SIZE>();` ? – chessbot Apr 27 '15 at 17:30
  • std::bitset has no conversion accepting an array of elements (where elements is the under-laying std::bitset type). In my point of view it is a failure as std::vector is. –  Apr 27 '15 at 17:36
  • Why the downvotes? Is it so unreasonable to want to be able to do this? – Barry Apr 27 '15 at 17:50

2 Answers2

10

std::bitset has no range constructor, so you will have to loop, but setting every bit individually with std::bitset::set() is underkill. std::bitset has support for binary operators, so you can at least set 64 bits in bulk:

  std::bitset<192> bs;

  for(int i = 2; i >= 0; --i) {
    bs <<= 64;
    bs |= flags[i];
  }

Update: In the comments, @icando raises the valid concern that bitshifts are O(N) operations for std::bitsets. For very large bitsets, this will ultimately eat the performance boost of bulk processing. In my benchmarks, the break-even point for a std::bitset<N * 64> in comparison to a simple loop that sets the bits individually and does not mutate the input data:

int pos = 0;
for(auto f : flags) {
  for(int b = 0; b < 64; ++b) {
    bs.set(pos++, f >> b & 1);
  }
}

is somewhere around N == 200 (gcc 4.9 on x86-64 with libstdc++ and -O2). Clang performs somewhat worse, breaking even around N == 160. Gcc with -O3 pushes it up to N == 250.

Taking the lower end, this means that if you want to work with bitsets of 10000 bits or larger, this approach may not be for you. On 32-bit platforms (such as common ARMs), the threshold will probably lie lower, so keep that in mind when you work with 5000-bit bitsets on such platforms. I would argue, however, that somewhere far before this point, you should have asked yourself if a bitset is really the right choice of container.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
  • If bs is bitset, then shift is an `O(N)` operation, so the overall loop is an `O(N^2)` operation. Nobody noticed this? – Kan Li Apr 27 '15 at 17:48
  • 1
    @Barry, what you said, "Or am I stuck having to call set() in a loop?", is actually a better solution than this. You accepted an O(N^2) solution given you have an O(N) solution yourself, which is weird. – Kan Li Apr 27 '15 at 18:18
  • That's a good point for large bitsets. I doubt the break-even point would be reached for a 192-bit bitset where the shift involves (on a PC) two machine words, but at some point it would. Let me go benchmark for a moment. – Wintermute Apr 27 '15 at 18:49
  • @icando Benchmark results are in; break-even above 10 kbits. I noted your concern and when it becomes relevant in the answer; it'll be good to have there if someone comes along who ponders abusing `std::bitset` in this way. For sane use cases, though, I'm not convinced that the threshold will ever be reached. – Wintermute Apr 27 '15 at 19:28
  • @Wintermute Cool. I'm dealing with pretty small `N`s and was mainly interested in code clarity rather than performance, so win-win! Also, <3 Neuromancer. – Barry Apr 28 '15 at 02:54
-1

If initializing from range is important, you might consider using std::vector

It does have constructor from pair of iterators

Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64