2016-03-11 4 views
9

注意:この質問の目的は、Perlのビット演算子をよりよく理解することです。私は数値Uを計算する方法を知っています。nがワードサイズに等しい場合でも動作する最低nビットを設定する式

$iを負でない整数とします。私は、その$i最下位ビットがすべて1つのであり、その残りのビットすべて0のあるunsigned int型U、と評価される単純な式E<$i> を探しています。例えば。 E<8>$iはマシンのワードサイズ(W)に等しい場合、E<$i>~0 を等しくする必要があり、具体的には255でなければなりません。

表現(1 << $i) - 1~(~0 << $i)両方が$iはどちらもむしろ~0よりも、値0を取る、その場合には W に等しいとき以外は、正しいことを行います。

私は計算を必要としないこれを行う方法を探していますW最初。


EDIT:(。もちろん、また非現実的)OK、私は解決策

$i < 1 ? 0 : do { my $j = 1 << $i - 1; $j < $j << 1 ? ($j << 1) - 1 : ~0 } 

または

$i < 1 ? 0 : (1 << ($i - 1)) < (1 << $i) ? (1 << $i) - 1 : ~0 

をplodding、醜い考え


私は奇妙な表記E<$i>を「表現式$i」の略語として使用しています。

私は$iは厳密に W より大きい場合E<$i>がに評価すべきもののため、現時点で強い好みを持っていません。

+3

"この質問の目的は、Perlのビット演算子をよりよく理解することです。"本当にそれらを理解したいのであれば、それらがどのように実装されているかを見てください:[pp.c]の 'pp_left_shift'(https://github.com/Perl/perl5/blob/maint-5.22/pp.c)を見てください。 #L1918)。 'pp_left_shift'は単にCの左シフト演算子を使います。 Cでは、それが含むビット数で値をシフトするための未定義の振る舞いで、 '(1 << $ i) - 1'と'〜(〜0 << $ i) 'は失敗すると考えられます。 – ThisSuitIsBlackNot

+0

'E <$i>'は 'f($ i)'の奇妙な表記です – ikegami

+1

@ikegami:私の意図は*機能表記を避けることでした。私は表現を求めていました。もちろん、 'E <$i>' *は '$ i'の関数であり、より一般的な/数学的な意味で使われますが、' E($ i) 'を使っていれば、私は 'sub E {...}'という形になっていたでしょう。 – kjo

答えて

2

場合(倍精度浮動小数点数と64ビットのintを使用するものを除く)eval($Config{nv_overflows_integers_at}) >= 2**($Config{ptrsize*8})、全てのシステムで

2**$i - 1 

(int(2**$i) - 1)|0 
  • I < W 、intは、NVをIV/UVに変換し、NVの精度がUVのサイズより小さいシステムで減算が機能するようにします。この場合、|0は効果がありません。

  • ≥Wの場合、intは効果がないため、減算は効果がありません。したがって|0はオーバーフローします。その場合、Perlは最大の整数を返します。

私は|0行動がどのように信頼性があることを知りません。これはコンパイラ固有のものです。これを使わないでください!

0

perlopのシフト演算子に関するドキュメントには、use bigint;という回答があります。

ドキュメントから:

Perlで両方<<>>use integer場合Cで<<>>を使用して直接実装されていることを注記(Integer Arithmetic参照)力で次に符号なし他、C整数が使用されている署名されCの整数が使用されます。いずれにしても、Perlの整数型(32ビットまたは64ビット)のサイズよりも大きな結果は生成されません。

整数の範囲をオーバーフローさせた結果は、Cでも未定義なので未定義です。つまり、32ビットの整数を使用すると、1 << 32は未定義です。負のビット数によるシフトも未定義です。

あなたのプラットフォームのネイティブ整数を受けることに飽きた場合は、use bigintプラグマはきちんと完全に問題を回避し:

print 20 << 20; # 20971520 
print 20 << 40; # 5120 on 32-bit machines, 
       # 21990232555520 on 64-bit machines 
use bigint; 
print 20 << 100; # 25353012004564588029934064107520 
+0

Cの符号なし整数のオーバーフローは、定義された振る舞いを持ち、単純に「ラップアラウンド」します。 Perlは、整数プラグマが有効でない限り、ビット演算子を使用するときは、伝統的に常に符号なし整数を使用しています(まだ使用しています)。 – chansen

+0

@ThisSuitIsBlackそうではありません。あなたが編集で追加した不要なドキュメントの一部を削除する必要があります。最後の文を除く最初の段落は、perldocへのポインタで十分です。 – chansen

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 
+0

ニース。'$ n = 0'で失敗しますが、それはチェックするのが簡単な例外です。 – kjo

+0

@ThisSuitIsBlackいいえ、編集していただきありがとうございます。 – chansen

+0

@kjo、ビット数がゼロの場合にどれくらいのデータを保存できますか? – chansen

2
use Config qw(%Config); 

$i >= $Config{uvsize}*8 ? ~0 : ~(~0 << $i) 

技術的には、ワードサイズは計算されない、見上げています。システムで

関連する問題