【问题标题】: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 之外的所有位都会打开,从而显示数据类型可以容纳的最大值。

【问题讨论】:

标签: c++ bit-shift


【解决方案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。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-12
        • 2019-01-28
        • 1970-01-01
        相关资源
        最近更新 更多