Why doesn't left bit shift << shift beyond 31 for long int datatype?

假如想象 提交于 2019-11-28 10:13:12

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.

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;
}

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.

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.

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.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!