5

I tried running the following code code:

char c = (2 << 7) >> 7

which should return 0 because 2 has this binary representation as a char:

0 0 0 0 0 0 1 0

After 7 shifts left, we get

0 0 0 0 0 0 0 0

Then, after seven shifts right, we get

0 0 0 0 0 0 0 0

However, I'm getting the result as 2, not 0.

The compiler says that 2 << 7 is 256, but it's a char and so it shouldn't be 256.

I understand that the 2 << 7 will be calculated as ints and the answer will be put into c so 256 >> 7 is 2.

I tried to cast 2 to char (ex: (char)2>>7) but it doesn't work either.

I'm trying to extract each bit from the char, so I wrote this code:

char c = 0x02;
for(int i=0;i<7;i++)
{
    char current = (c<<i)>>7;
}

How can I get each bit? What's wrong with my way?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
MyNick
  • 536
  • 1
  • 9
  • 25
  • 6
    `2<<7` is *not* a `char`. – Oliver Charlesworth Jun 07 '14 at 19:00
  • 2
    Please don't replace your question text with "fixed it thanks" if you figure out what's wrong. Instead, add an answer explaining what was wrong. – templatetypedef Jun 07 '14 at 19:03
  • I forget but this is a dupe for sure. – haccks Jun 07 '14 at 19:07
  • @haccks well [this is similar](http://stackoverflow.com/questions/22702091/why-does-combing-two-shifts-of-a-uint8-t-produce-a-different-result/22702107#22702107) but not exactly a dup. I am sure you will find a bunch of similar ones. I suppose it depends on how strict you are with the term dup. – Shafik Yaghmour Jun 07 '14 at 19:44
  • Even if you cast the first operand like `(char)2>>7`, it won't work because of default promotion rule. All types narrower than int will be automatically promoted to int and there's no way to do maths on char or short, etc, except on a non standard compiler – phuclv Jun 08 '14 at 01:53

3 Answers3

7

The result of an arithmetic shift operation with one operand being an int in C++ is always an int. Therefore, when you write

current = (c << i) >> 7;

C++ will interpret (c << i) and (c << i) >> 7 as ints, casting back to a char only when the assignment is done. Since the temporary values are ints, no overflow occurs and the result should come out to the integer result casted to a char.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • How come int to char conversion (demotion) is not warned in c++? Why doesnt it require explicit casting? C++ is type strict right? – SwiftMango Jun 07 '14 at 19:58
  • @texasbruce C++ permits implicit conversions between its primitive integer types without explicit casts. If you crank up the warning settings on your compiler, you might get a warning about it, though it's not required. While C++ does have static typing, since it explicitly allows these conversions, there's no type error. – templatetypedef Jun 07 '14 at 20:02
  • "one operand" --> "the left operand". The type of the right operand doesn't make a difference – M.M Dec 30 '15 at 19:37
4

To get each bit, you could write:

(c >> i) & 0x01

Advantage: It works for any integer type.

JohnB
  • 13,315
  • 4
  • 38
  • 65
  • 1
    Yes this is the way to do. On some platforms this is just a single bit test instruction. Shifting over the type's max may invoke undefined behavior – phuclv Jun 08 '14 at 01:55
4

According to 5.8 [expr.shift] paragraph 1:

... The operands shall be of integral or unscoped enumeration type and integral promotions are performed. The type of the result is that of the promoted left operand. ...

This for a left argument of type char together with the rules on integer promotion (4.5 [conv.prom]) says that the result is int. Of course, an int can hold the result of 2 << 7. You can easily verify this behavior, too:

#include <iostream>

void print(char c) { std::cout << "char=" << int(c) << "\n"; }
void print(int i) { std::cout << "int=" << i << "\n"; }

int main()
{
    print(2 << 7);
}

The most simple approach to get the bits of a value is to use a std::bitset<N> with N being the digits of the corresponding unsigned type, e.g.:

char c('a');
std::bitset<std::numeric_limits<unsigned char>::digits> bits(c);

If you want to get bits yourself you'd mask the bits using its unsigned counterpart of the integer type, e.g.:

template <typename T>
void get_bits(T val)
{
    typedef typename std::make_unsigned<T>::type U;
    U value(val);
    for (std::size_t s(std::numeric_limits<U>::digits); s-- != 0; ) {
        std::cout << bool(value & (1u << s));
    }
    std::cout << '\n';
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380