为啥 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 <limits>
和 std::numeric_limits<long int>::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】:
虽然您的x
是long int
类型,但1
不是。 1
是一个int
,所以1<<63
确实是未定义的。
按照 Wojtek 的建议尝试 (static_cast<long int>(1) << 63)
或 1L << 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<unsigned long int>::max()
来构造std::numeric_limits<long int>::max()
。那是相当没用的。
在第一个例子中,max
是全比特的,所以在上面做&
没什么意义,只要max >> 1
就可以了:)【参考方案3】:
您的标题具有误导性;如果long
确实那么大,则long
可以移动超过31
位。但是,您的代码转移了1
,即int
。
在 C++ 中,表达式的类型由表达式本身决定。无论如何,表达式XXXXX
具有相同的类型;如果你以后去double foo = XXXXX;
,这并不意味着XXXXX
是双精度的——这意味着从XXXXX
到double
的转换发生。
如果您想长时间左移,请明确执行此操作,例如1L << 32
,或((long)1) << 32
。请注意,long
的大小因平台而异,因此如果您不希望代码在不同系统上运行时中断,那么您将不得不采取进一步措施,例如使用固定宽度类型或移位CHAR_BIT * sizeof(long) - 1
.
您的预期代码还有另一个问题:如果 long
是 64 位或更少,1L << 63
会导致未定义的行为。这是因为有符号整数溢出;左移的定义与重复乘以 2 相同,因此尝试“移入符号位”会导致溢出。
要解决此问题,请在可以转换到 MSB 的情况下使用无符号类型,例如1ul << 63
。
从技术上讲,如果您不在 2 的补码系统上,~0
不会做您想做的事情,但现在忽略这种情况是很安全的。
通过long x = ~0 & ~(1 << 63)
查看您的总体意图。更短的写法是:
long x = LONG_MAX;
由<climits>
定义。如果你想在所有平台上使用 64 位,那么
int64_t x = INT64_MAX;
注意。如果您不打算使用负值,请分别使用 unsigned long x
和 uint64_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<long int>(1) << (std::numeric_limits<long int>::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 类型参数的重载函数?