0

How can I convert given bitset of a length N (where 0 < N < 64) to signed int. For instance, given:

std::bitset<13> b("1111111101100");

I would like to get back the value -20, not 8172.

My approach:

int t = (static_cast<int>(b.to_ullong()));
if(t > pow(2, 13)/2)
    t -= pow(2, 13);

Is there a more generic way to approach this?

Edit: Also the bitset is actually std::bitset<64> and the N can be run-time known value passed by other means.

Öö Tiib
  • 10,809
  • 25
  • 44
carobnodrvo
  • 1,021
  • 1
  • 9
  • 32
  • And does it work as intended? – πάντα ῥεῖ Mar 07 '16 at 17:22
  • It does, but as you can see it's not really clear what does it do. So my question actually is, is there "cleaner" way to do it? – carobnodrvo Mar 07 '16 at 17:27
  • 1
    Signed int contains in majority of implementations less bits of information than bitset with length 64 so your conversion has to be lossy. – Öö Tiib Mar 07 '16 at 17:27
  • 1
    You should use an integer with at least 64 bit, e.g. `int64_t`. There is no problem with signs because you stated that the length N will be smaller than 64, – knivil Mar 07 '16 at 17:33
  • @ÖöTiib theoretically there could occur 64bit number but in 99% cases number is smaller then 20bits (13 is most common). So actually I didn't test all cases. – carobnodrvo Mar 07 '16 at 17:33

2 Answers2

0

We can write a function template to do this for us:

template <size_t N, class = std::enable_if_t<(N > 0 && N < 64)>
int64_t as_signed(const std::bitset<N>& b)
{
    int64_t v = b.to_ullong(); // safe since we know N < 64
    return b[N-1] ? ((1LL << N) - v) : v;
}
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Shia LaBeouf is that you :). This won't work. Counterexample: `std::bitset<63> bs(std::string("1111111101100"));` (13 bit signed number) will be 8172 instead of -20. – carobnodrvo Mar 07 '16 at 17:47
  • @carobnodrvo Didn't realize that was what you wanted, edited that into your question and changed my answer. – Barry Mar 07 '16 at 17:59
0

Perhaps best is to let compiler to sign-extend it itself:

struct S { int64_t x:N; } s;
int64_t result = s.x = b.to_ullong();

Compiler likely optimizes that s out.

It must be is safe since the int64_t (where available) is required to be two's complement.

Edit: When the actual bit count to extend is only known run-time then most portable algorithm is with mask:

// Do this if bits above position N in b may be are not zero to clear those.
int64_t x = b.to_ullong() & ((1ULL << N) - 1);
// Otherwise just 
int64_t x = b.to_ullong();

int64_t const mask = 1ULL << (N - 1);
int64_t result = (x ^ mask) - mask;

A slightly faster but less portable method with dynamic bit counts is with bit shifts (works when architecture has signed arithmetic right shift):

int const shift = 64 - N;
int64_t result = ((int64_t)b.to_ullong() << shift) >> shift;
Öö Tiib
  • 10,809
  • 25
  • 44