我在C#中有以下代码行:
ulong res = (1<<(1<<n))-1;
表示某个整数n。
只要n小于5,我就会得到正确答案。 但是,对于n> = 5,它不起作用。
任何想法,使用按位运算符,即使n = 5和n = 6,如何得到正确的答案? 对于n = 6,结果应为~0UL,对于n = 5,结果应为0xFFFFFFFF。
答案 0 :(得分:11)
只要n小于5,我就会得到正确答案。但是,对于n> = 5,它不起作用。
嗯,它遵守规范。来自C#5规范的第7.9节:
&lt;&lt;运算符将x向左移位如下所述计算的位数。
对于预定义的运算符,要移位的位数计算如下:
- 当
x
的类型为int
或uint
时,移位计数由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
的类型为long
或ulong
时,移位计数由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];