Convert bitset<a> to signed int, with a<32>

4.6k views Asked by At

I was reading the question convert bitset to int in c++ and thought, hey, that doesn't work, I've already tried it. But then I started trying and quickly i discovered that:

#include <bitset>
#include <iostream>
int main()
{
   std::bitset<31> b31(-1);
   std::bitset<32> b32(-1);
   std::cout<<static_cast<int>(b31.to_ulong())<<std::endl;
   std::cout<<static_cast<int>(b32.to_ulong())<<std::endl;
   return 0;
}

gives the output

2147483647
-1

So, how do I cast bitsets smaller than 32 to signed integers?

CLARIFICATION: I want to cast back to signed int with the 10-base value used when constructing the bitsets preserved. "store signed ints in bitsets"

I have tried this with gcc 4.6.2, mingw32 on win 7, and the same result is obtained with c-style cast (int)

3

There are 3 answers

0
NPE On BEST ANSWER

How about something along these lines:

#include <bitset>
#include <iostream>

template<std::size_t B>
long bitset_to_long(const std::bitset<B>& b) {
  struct {long x:B;} s;
  return s.x = b.to_ulong();
}

int main()
{
   std::bitset<31> b31(-1);
   std::bitset<32> b32(-1);
   std::cout << bitset_to_long(b31) << std::endl;
   std::cout << bitset_to_long(b32) << std::endl;
   return 0;
}

(Inspired by Sign extending from a constant bit-width.)

3
Sergey Kalinichenko On

Make a mask that has ones in the remaining upper bits, and OR it with the unsigned value from the cast when the most significant bit in the bit set is set to 1, like this:

const int sz = 15;
std::bitset<sz> b(-1);
int num = static_cast<int>(b.to_ulong());
if (b[sz-1]) {
    int mask = (1<<sz)-1;
    num |= ~mask;
}
std::cout << num << std::endl;

Expression (1<<sz) makes a number with only sz's bit set. (1<<sz)-1 makes a mask with the last sz-1 bits set to 1s. ~ inverts it, producing a binary complement to the value of your bit set.

Demo.

1
phuclv On

You need sign extension

#define BITLEN 31
#define SHIFT_AMOUNT (sizeof(int)*CHAR_BIT - BITLEN)

std::bitset<BITLEN> b31(-1);
int v = (static_cast<int>(b31.to_ulong()) << SHIFT_AMOUNT) >> SHIFT_AMOUNT;
std::cout << v << std::endl;

Technically right shift on signed types are implementation defined, but all modern implementations do an arithmetic shift so it's not a real issue. However if you really want to avoid that behavior then use the portable alternative from the famous bit twiddling hacks

template <typename T, unsigned B>
inline T signextend(const T x)
{
    struct {T x: B;} s;
    return s.x = x;
}

signextend<int, 31>(b31.to_ulong());