设置最低n位的表达式即使在n等于字大小

时间:2016-03-11 20:24:42

标签: perl

注意:这个问题的目的是更好地理解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

4 个答案:

答案 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 int将NV转换为IV / UV,允许减法在NV的精度小于UV的大小的系统上工作。 |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