Why doesn't left bit shift << shift beyo

2020-02-01 02:04发布

I want to use the following code in my program but gcc won't allow me to left shift my 1 beyond 31.

sizeof(long int) displays 8, so doesn't that mean I can left shift till 63?

#include <iostream>

using namespace std;

int main(){
    long int x;
    x=(~0 & ~(1<<63));
    cout<<x<<endl;
    return 0;
}

The compiling outputs the following warning:

left shift `count >= width` of type [enabled by default] `x=(~0 & ~(1<<63))`;
                                                                    ^

and the output is -1. Had I left shifted 31 bits I get 2147483647 as expected of int.

I am expecting all bits except the MSB to be turned on thus displaying the maximum value the datatype can hold.

标签: c++ bit-shift
5条回答
Root(大扎)
2楼-- · 2020-02-01 02:39

Although your x is of type long int, the 1 is not. 1 is an int, so 1<<63 is indeed undefined.

Try (static_cast<long int>(1) << 63), or 1L << 63 as suggested by Wojtek.

查看更多
smile是对你的礼貌
3楼-- · 2020-02-01 02:44

Your title is misleading; a long can shift beyond 31 bits if a long is indeed that big. However your code shifts 1, which is an int.

In C++, the type of an expression is determined by the expression itself. An expression XXXXX has the same type regardless; if you later go double foo = XXXXX; it doesn't mean XXXXX is a double - it means a conversion happens from whatever XXXXX was, to double.

If you want to left-shift a long, then do that explicitly, e.g. 1L << 32, or ((long)1) << 32. Note that the size of long varies between platforms, so if you don't want your code to break when run on a different system then you'll have to take further measures, such as using fixed-width types, or shifting by CHAR_BIT * sizeof(long) - 1.

There is another issue with your intended code: 1L << 63 causes undefined behaviour if long is 64-bit or less. This is because of signed integer overflow; left-shift is defined the same as repeated multiplication of two, so attempting to "shift into the sign bit" causes an overflow.

To fix this, use unsigned types where it is fine to shift into the MSB, e.g. 1ul << 63.

Technically there is another issue in that ~0 doesn't do what you want if you are not on a 2's complement system, but these days it's pretty safe to ignore that case.

Looking at your overall intention with long x = ~0 & ~(1 << 63). A shorter way to write this is:

long x = LONG_MAX;

which is defined by <climits>. If you wanted 64-bit on all platforms then

int64_t x = INT64_MAX;

NB. If you do not intend to work with negative values then use unsigned long x and uint64_t respectively.

查看更多
来,给爷笑一个
4楼-- · 2020-02-01 02:45

The default datatype for a numeric value in C is integer unless explicitly mentioned.

Here you have to type cast the 1 as long int which would otherwise be an int.

查看更多
倾城 Initia
5楼-- · 2020-02-01 02:54

First let me state a few things about the shift, which is the source of your problem:

There is no guarantee that long int is actually 64 bit wide.

The most generic way I can think of is using std::numeric_limits:

static_cast<long int>(1) << (std::numeric_limits<long int>::digits - 1);

Now you can even make that a constexpr templated function:

template <typename Integer>
constexpr Integer foo()
{
    return static_cast<Integer>(1) << (std::numeric_limits<Integer>::digits - 1);
}

So replacing the shift with static_cast<long int>(1) << (std::numeric_limits<long int>::digits - 1) will fix your issue, however there is a far better way:

std::numeric_limits includes a bunch of useful stuff, including:

std::numeric_limits<T>::max(); // the maximum value T can hold
std::numeric_limits<T>::min(); // the minimum value T can hold
std::numeric_limits<T>::digits; // the number of binary digits
std::numeric_limits<T>::is_signed(); // well, do I have to explain? ;-)

See cppreference.com for a complete list. You should prefer the facilities provided by the standard library, because it will most likely have fewer mistakes and other developers immediately know it.

查看更多
萌系小妹纸
6楼-- · 2020-02-01 03:02

You can't use 1 (int by default) to shift it beyond the int boundaries.

There's an easier way to get the "all bits except the MSB turned on" for a specific datatype

#include <iostream>
#include <limits>

using namespace std;

int main(){
    unsigned long int max = std::numeric_limits<unsigned long int>::max();
    unsigned long int max_without_MSB = max >> 1;
    cout<< max_without_MSB <<endl;
    return 0;
}

note the unsigned type. Without numeric_limits:

#include <iostream>
using namespace std;

int main() {

    long int max = -1;
    unsigned long int max_without_MSB = ((unsigned long int)max) >> 1;
    cout << max_without_MSB << endl;

    return 0;
}
查看更多
登录 后发表回答