为啥 long int 数据类型的左位移 << 位移超过 31?

Posted

技术标签:

【中文标题】为啥 long int 数据类型的左位移 << 位移超过 31?【英文标题】:Why doesn't left bit shift << shift beyond 31 for long int datatype?为什么 long int 数据类型的左位移 << 位移超过 31? 【发布时间】:2014-06-09 10:06:00 【问题描述】:

我想在我的程序中使用以下代码,但 gcc 不允许我将 1 左移超过 31。

sizeof(long int) 显示 8,那是不是意味着我可以左移到 63?

#include <iostream>

using namespace std;

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

编译输出如下警告:

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

输出为-1。如果我左移 31 位,我会得到 2147483647,正如预期的 int。

我希望除了 MSB 之外的所有位都会打开,从而显示数据类型可以容纳的最大值。

【问题讨论】:

#include &lt;limits&gt;std::numeric_limits&lt;long int&gt;::max() ?您假设您知道实现如何存储有符号整数。那不是便携式的。 char 也不能保证是 8 位。 @tim-seguine:被低估的评论。 bit shifting with unsigned long type produces wrong results, left shift on 64 bits fail, how to use uint64_t in C 【参考方案1】:

虽然您的xlong int 类型,但1 不是。 1 是一个int,所以1&lt;&lt;63 确实是未定义的。

按照 Wojtek 的建议尝试 (static_cast&lt;long int&gt;(1) &lt;&lt; 63)1L &lt;&lt; 63

【讨论】:

为什么不用1L 而不是演员? 如果 long 是 64-btw(整数溢出),这仍然是未定义的行为。 long 实际上不是总是 32 位吗? @FredOverflow long 在(大多数)Linux/Unix 上是 64 位【参考方案2】:

您不能使用 1(默认为 int)将其移出 int 边界。

有一种更简单的方法可以让特定数据类型的“除了 MSB 之外的所有位都打开”

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

注意无符号类型。没有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;

【讨论】:

这不依赖于数据表示吗? (二的补码与一的补码?) numeric_limits 在 C++98 中 马特是正确的,我会解决的。这也应该适用于一个人的补充 @MarcoA。无论如何,这种方法就像用房子盖房子:你正在使用std::numeric_limits&lt;unsigned long int&gt;::max() 来构造std::numeric_limits&lt;long int&gt;::max()。那是相当没用的。 在第一个例子中,max 是全比特的,所以在上面做&amp; 没什么意义,只要max &gt;&gt; 1 就可以了:)【参考方案3】:

您的标题具有误导性;如果long 确实那么大,则long 可以移动超过31 位。但是,您的代码转移了1,即int

在 C++ 中,表达式的类型由表达式本身决定。无论如何,表达式XXXXX 具有相同的类型;如果你以后去double foo = XXXXX;,这并不意味着XXXXX 是双精度的——这意味着从XXXXXdouble 的转换发生。

如果您想长时间左移,请明确执行此操作,例如1L &lt;&lt; 32,或((long)1) &lt;&lt; 32。请注意,long 的大小因平台而异,因此如果您不希望代码在不同系统上运行时中断,那么您将不得不采取进一步措施,例如使用固定宽度类型或移位CHAR_BIT * sizeof(long) - 1.

您的预期代码还有另一个问题:如果 long 是 64 位或更少,1L &lt;&lt; 63 会导致未定义的行为。这是因为有符号整数溢出;左移的定义与重复乘以 2 相同,因此尝试“移入符号位”会导致溢出。

要解决此问题,请在可以转换到 MSB 的情况下使用无符号类型,例如1ul &lt;&lt; 63

从技术上讲,如果您不在 2 的补码系统上,~0 不会做您想做的事情,但现在忽略这种情况是很安全的。

通过long x = ~0 &amp; ~(1 &lt;&lt; 63) 查看您的总体意图。更短的写法是:

long x = LONG_MAX;

&lt;climits&gt; 定义。如果你想在所有平台上使用 64 位,那么

int64_t x = INT64_MAX;

注意。如果您不打算使用负值,请分别使用 unsigned long xuint64_t

【讨论】:

【参考方案4】:

首先让我说明一些关于转变的事情,这是您问题的根源:

不能保证long int 实际上是 64 位宽。

我能想到的最通用的方法是使用std::numeric_limits

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

现在您甚至可以将其设为 constexpr 模板函数:

template <typename Integer>
constexpr Integer foo()

    return static_cast<Integer>(1) << (std::numeric_limits<Integer>::digits - 1);


因此,用static_cast&lt;long int&gt;(1) &lt;&lt; (std::numeric_limits&lt;long int&gt;::digits - 1) 替换班次将解决您的问题,但是还有更好的方法:

std::numeric_limits 包含一堆有用的东西,包括:

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? ;-)

有关完整列表,请参阅cppreference.com。您应该更喜欢标准库提供的工具,因为它很可能会出现更少的错误,并且其他开发人员会立即知道这一点。

【讨论】:

【参考方案5】:

除非明确提及,否则 C 中数值的默认数据类型是整数。

在这里,您必须将 1 类型转换为 long int,否则它将是一个 int。

【讨论】:

以上是关于为啥 long int 数据类型的左位移 << 位移超过 31?的主要内容,如果未能解决你的问题,请参考以下文章

位移&二进制转换&原码&反码&补码

为啥在传递 long long 时调用具有两个 double 类型参数的重载函数?

为啥在 x64 Java 中 long 比 int 慢?

java long型计算问题

为啥Java中BitSet的内部数据存储为long[]而不是Java中的int[]?

Java中short类型想加为啥会成为int型