【问题标题】:Weird result after assigning 2^31 to a signed and unsigned 32-bit integer variable将 2^31 分配给有符号和无符号 32 位整数变量后的奇怪结果
【发布时间】:2012-04-15 22:21:02
【问题描述】:

正如问题标题所示,将 2^31 分配给有符号和无符号 32 位整数变量会产生意想不到的结果。

这是我制作的短程序(C++),看看发生了什么:

#include <cstdio>
using namespace std;

int main()
{
    unsigned long long n = 1<<31;
    long long n2 = 1<<31;  // this works as expected
    printf("%llu\n",n);
    printf("%lld\n",n2);
    printf("size of ULL: %d, size of LL: %d\n", sizeof(unsigned long long), sizeof(long long) );
    return 0;
}

这是输出:

MyPC / # c++ test.cpp -o test
MyPC / # ./test
18446744071562067968      <- Should be 2^31 right?
-2147483648               <- This is correct ( -2^31 because of the sign bit)
size of ULL: 8, size of LL: 8

然后我向它添加了另一个函数p()

void p()
{
  unsigned long long n = 1<<32;  // since n is 8 bytes, this should be legal for any integer from 32 to 63
  printf("%llu\n",n);
}

在编译和运行时,这让我更加困惑:

MyPC / # c++ test.cpp -o test
test.cpp: In function ‘void p()’:
test.cpp:6:28: warning: left shift count >= width of type [enabled by default]
MyPC / # ./test 
0
MyPC /

为什么编译器会抱怨左移计数太大? sizeof(unsigned long long) 返回 8,那么这是否意味着 2^63-1 是该数据类型的最大值?

让我印象深刻的是,也许 n*2 和 n

void s()
{
   unsigned long long n = 1;
   for(int a=0;a<63;a++) n = n*2;
   printf("%llu\n",n);
}

这给出了正确的 2^63 值作为输出,即9223372036854775808(我使用 python 验证了它)。但是左撇子有什么问题呢?

算术左移 n 相当于乘以 2n (前提是值不溢出)

-- 维基百科

该值没有溢出,只有一个减号会出现,因为该值为 2^63(所有位都已设置)。

我仍然无法弄清楚左移发生了什么,有人可以解释一下吗?

PS:这个程序是在运行 linux mint 的 32 位系统上运行的(如果有帮助的话)

【问题讨论】:

标签: c++ bit-shift unsigned-integer


【解决方案1】:

在这一行:

unsigned long long n = 1<<32;

问题在于文字 1 的类型为 int - 这可能只有 32 位。因此,这种转变会将其推出界限。

仅仅因为您存储到更大的数据类型并不意味着表达式中的所有内容都以更大的大小完成。

因此,要更正它,您需要将其转换为 unsigned long long 文字:

unsigned long long n = (unsigned long long)1 << 32;
unsigned long long n = 1ULL << 32;

【讨论】:

  • 关于最后一个建议:使用大写字母作为类型指示符。对于许多字体,区分1ll111 可能很困难,如果不是不可能的话; 1LL 清晰明确(并且没有后缀 O 会导致 0 出现问题)。
【解决方案2】:

1 &lt;&lt; 32 失败的原因是因为1 没有正确的类型(它是int)。在赋值本身实际发生之前,编译器不会执行任何转换魔法,因此 1 &lt;&lt; 32 使用 int 算术计算,给出溢出警告。

尝试使用1LL1ULL 代替它们分别具有long longunsigned long long 类型。

【讨论】:

    【解决方案3】:

    线

    unsigned long long n = 1<<32;
    

    导致溢出,因为字面量 1 是 int 类型,所以 1 &lt;&lt; 32 也是一个 int,在大多数情况下是 32 位。

    线

    unsigned long long n = 1<<31;
    

    出于同样的原因也会溢出。请注意,1 的类型为signed int,因此它实际上只有 31 位的值和 1 位的符号。所以当你移位1 &lt;&lt; 31时,它会溢出值位,导致-2147483648,然后将其转换为无符号long long,即18446744071562067968。如果您检查变量并转换它们,您可以在调试器中验证这一点。

    所以用

    unsigned long long n = 1ULL << 31;
    

    【讨论】:

      猜你喜欢
      • 2015-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-31
      相关资源
      最近更新 更多