4

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)

AAEM
  • 1,837
  • 2
  • 18
  • 26
Schaki
  • 1,697
  • 1
  • 12
  • 14
  • 1
    Fwiw, this produces `-1 -1` on OSX x64 with clang 3.5 – WhozCraig Dec 09 '14 at 12:08
  • Ok nice! I was about to ask if i had misunderstood the `to_ulong()` specifications or if it was compiler specific. Would be interesting to know how other compilers handle this, eg MSVC, intel if the reader bothers to try ;) – Schaki Dec 09 '14 at 12:16

3 Answers3

2

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.)

NPE
  • 486,780
  • 108
  • 951
  • 1,012
1

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.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
0

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());
phuclv
  • 37,963
  • 15
  • 156
  • 475
  • Right-shifting a negative number has an implementation-defined result, so this may not work on all platforms. – Ben Voigt Apr 28 '18 at 04:13