带左移的C#中的整数溢出

时间:2013-06-26 20:20:36

标签: c#

我在C#中有以下代码行:

ulong res = (1<<(1<<n))-1;

表示某个整数n。

只要n小于5,我就会得到正确答案。 但是,对于n> = 5,它不起作用。

任何想法,使用按位运算符,即使n = 5和n = 6,如何得到正确的答案? 对于n = 6,结果应为~0UL,对于n = 5,结果应为0xFFFFFFFF。

3 个答案:

答案 0 :(得分:11)

  

只要n小于5,我就会得到正确答案。但是,对于n> = 5,它不起作用。

嗯,它遵守规范。来自C#5规范的第7.9节:

  

&lt;&lt;运算符将x向左移位如下所述计算的位数。

     

对于预定义的运算符,要移位的位数计算如下:

     
      
  • x的类型为intuint时,移位计数由count的低位五位给出。换句话说,移位计数是从count & 0x1F计算的。
  •   

所以当n为5时,1 << n(内移)为32.所以你已经有效了:

int x = 32;
ulong res = (1 << x) - 1;

现在32 & 0x1f为0 ...因此(1 << 0) - 1为0。

现在,如果你按照p.s.w.g的建议制作“外部”移位运算符1UL的第一个操作数,那么你就会遇到规范的这一部分:

  
      
  • x的类型为longulong时,移位计数由count的低位六位给出。换句话说,移位计数是从count & 0x3F计算的。
  •   

所以代码会按照您的预期进行,至少对于n = 5 - 但不是n = 6.

答案 1 :(得分:5)

我认为问题是常量1被认为是System.Int32所以它假定你想要操作的数据类型,但它很快溢出了该数据类型的边界。如果您将其更改为:

ulong res = (1ul<<(1<<n))-1;

它对我有用:

var ns = new[] { 0, 1, 2, 3, 4, 5, 6 };
var output = ns.Select(n => (1ul<<(1<<n))-1); 
// { 0x1ul, 0x3ul, 0xful, 0xfful, 0xfffful, 0xfffffffful, 0ul }

答案 2 :(得分:4)

问题是文字“1”是32位有符号整数,而不是64位无符号长整数。当n为5或更大时,你超过了32位整数的范围。

更改适当的1到1UL可以解决问题,适用于n = 5(但不是n = 6,超出了ulong的范围)。

ulong res = (1UL<<(1<<n))-1;

使n = 6工作(即获得0xFFFFFFFFFFFFFFFF)并不容易。一个简单的解决方案是使用BigInteger,它将消除64位整数没有定义64位位移的问题。

// (reference and using System.Numerics)
ulong res = (ulong)(BigInteger.One<<(1<<n)-1)

但是,这不会特别快。也许是一系列常数?

var arr = new[] {0x1, 0x3, 0xF, 0xFF, 0xFFFF, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF};
ulong res = arr[n];