注意:这个问题的目的是更好地理解Perl的按位运算符。我知道如何计算下面描述的数字 U 。
设$i
为非负整数。我正在寻找一个简单的表达式E<$i>
1 ,它将评估 unsigned int U ,其$i
最低位均为1,其余位均为0。例如。 E<8>
应为255.特别是,如果$i
等于机器的字大小( W ),E<$i>
应该等于~0
2
表达式(1 << $i) - 1
和~(~0 << $i)
都做正确的事情,除非$i
等于 W ,在这种情况下它们都采用值{{ 1}},而不是0
。
我正在寻找一种不需要先计算 W 的方法。
编辑:好的,我想到了一个丑陋,沉闷的解决方案
~0
或
$i < 1 ? 0 : do { my $j = 1 << $i - 1; $j < $j << 1 ? ( $j << 1 ) - 1 : ~0 }
(当然也是不切实际的。)
1 我使用奇怪的符号$i < 1 ? 0 : ( 1 << ( $i - 1 ) ) < ( 1 << $i ) ? ( 1 << $i ) - 1 : ~0
作为“基于E<$i>
的表达”的简写。
2 当$i
严格更大 E<$i>
应评估的内容时,我目前没有强烈偏好>比 W 。
答案 0 :(得分:2)
use Config qw( %Config );
$i >= $Config{uvsize}*8 ? ~0 : ~(~0 << $i)
从技术上讲,单词大小是查找的,而不是计算的。
答案 1 :(得分:2)
在eval($Config{nv_overflows_integers_at}) >= 2**($Config{ptrsize*8})
(不包括使用双精度浮点数和64位整数)的系统上,
2**$i - 1
在所有系统上,
( int(2**$i) - 1 )|0
当i |0
在这种情况下无效。
当i≥W时,int
没有效果,因此减法无效。因此|0
溢出,在这种情况下Perl返回最大的整数。
我不知道|0
行为有多可靠。它可能是特定于编译器的。不要使用它!
答案 2 :(得分:1)
有趣的挑战!
use Devel::Peek qw[Dump];
for my $n (8, 16, 32, 64) {
Dump(~(((1 << ($n - 1)) << 1) - 1) ^ ~0);
}
输出:
SV = IV(0x7ff60b835508) at 0x7ff60b835518
REFCNT = 1
FLAGS = (PADTMP,IOK,pIOK)
IV = 255
SV = IV(0x7ff60b835508) at 0x7ff60b835518
REFCNT = 1
FLAGS = (PADTMP,IOK,pIOK)
IV = 65535
SV = IV(0x7ff60b835508) at 0x7ff60b835518
REFCNT = 1
FLAGS = (PADTMP,IOK,pIOK)
IV = 4294967295
SV = IV(0x7ff60b835508) at 0x7ff60b835518
REFCNT = 1
FLAGS = (PADTMP,IOK,pIOK,IsUV)
UV = 18446744073709551615
Perl编译:
ivtype='long', ivsize=8, nvtype='double', nvsize=8
答案 3 :(得分:0)
perlop中有关移位运算符的文档可以解答您的问题:use bigint;
。
来自文档:
请注意,Perl中的
<<
和>>
都是使用C中的<<
和>>
直接实现的。如果use integer
(请参阅Integer Arithmetic )生效然后签名使用C整数,否则使用无符号C整数。无论哪种方式,实现都不会产生大于Perl构造的整数类型(32位或64位)的大小的结果。溢出整数范围的结果是未定义的,因为它在C中也是未定义的。换句话说,使用32位整数,
1 << 32
是未定义的。通过负位数移位也是不确定的。如果您厌倦了受到平台原生整数的约束,那么
use bigint
pragma会完全回避这个问题:print 20 << 20; # 20971520 print 20 << 40; # 5120 on 32-bit machines, # 21990232555520 on 64-bit machines use bigint; print 20 << 100; # 25353012004564588029934064107520