2016-03-11 14 views
9

NB: lo scopo di questa domanda è capire meglio gli operatori bitwise di Perl. Sono a conoscenza dei modi per calcolare il numero U descritto di seguito.Espressione per l'impostazione di n bit più bassi che funziona anche quando n è uguale alla dimensione della parola

Lascia che $i sia un numero intero non negativo. Sto cercando una semplice espressione E<$i> che valuterà al unsigned intU, la cui $i bit più bassi sono tutti 1, e il cui bit rimanenti sono tutti 0 di. Per esempio. E<8> dovrebbe essere 255. In particolare, se $i uguale dimensione della parola della macchina (W), E<$i> deve essere uguale ~0 .

Le espressioni (1 << $i) - 1 e ~(~0 << $i) entrambi fare la cosa giusta, se non quando $i uguale W, nel qual caso entrambi assumono valore 0, piuttosto che ~0.

Sto cercando un modo per farlo che non richiede il calcolo W prima.


EDIT: OK, ho pensato di un brutto, arrancando soluzione

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

o

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


(anche impraticabile, naturalmente.) Sto usando la notazione strana E<$i> come abbreviazione di "espressione basata su $i".

Non ho una forte preferenza al momento per quanto E<$i> dovrebbe valutare quando $i è strettamente maggiore di W.

+3

"lo scopo di questa domanda è quello di capire meglio gli operatori bitwise di Perl." Se vuoi veramente capirli, dovresti vedere come sono implementati: vedi 'pp_left_shift' in [pp.c] (https://github.com/Perl/perl5/blob/maint-5.22/pp.C# L1918). 'pp_left_shift' usa semplicemente l'operatore di spostamento a sinistra di C; in C, è un comportamento indefinito spostare un valore per il numero di bit che contiene, motivo per cui penso che '(1 << $ i) - 1' e' ~ (~ 0 << $ i) 'falliscono. – ThisSuitIsBlackNot

+0

'E 'una notazione strana per' f ($ i) ' – ikegami

+1

@ikegami: è perché la mia intenzione era * precisamente di evitare * notazione funzionale, dal momento che * sarebbe stato invariabilmente * erroneamente interpretato come la mia richiesta di un' sub ', mentre stavo chiedendo un'espressione. Certamente, 'E <$i>' * è * una funzione di '$ i', nel senso più generale/matematico del termine, ma se avessi usato' E ($ i) ', la maggior parte, se non tutte, le risposte Sarei diventato della forma 'sub E {...}', e questo non è * quello che sto cercando. – kjo

risposta

2

Nei sistemi in cui eval($Config{nv_overflows_integers_at}) >= 2**($Config{ptrsize*8}) (esclude quella che utilizza float precisione doppia e interi a 64 bit),

2**$i - 1 

In tutti i sistemi,

(int(2**$i) - 1)|0 
  • Quando i < W , int convertirà la NV in un IV/UV, consentendo alla sottrazione di lavorare su sistemi con la precisione di NVs inferiore alla dimensione dei raggi UV. |0 non ha alcun effetto in questo caso.

  • Quando i ≥ W, int non ha alcun effetto, la sottrazione non ha alcun effetto. |0 quindi overflow, nel qual caso Perl restituisce il numero intero più grande.

Non so come affidabile che |0 comportamento è. Potrebbe essere specifico del compilatore. Non usare questo!

0

La documentazione sugli operatori di turno in perlop ha una risposta al problema: use bigint;.

Dalla documentazione:

notare che sia << e >> in Perl sono implementati direttamente utilizzando << e >> in C. Se use integer (vedi Integer Arithmetic) è in vigore poi firmato C interi vengono utilizzati, altrimenti non firmato Si usano gli interi C. In entrambi i casi, l'implementazione non genererà risultati più grandi delle dimensioni del tipo intero in cui Perl è stato creato con (32 bit o 64 bit).

Il risultato dell'overflow dell'intervallo degli interi non è definito perché non è definito anche in C.In altre parole, utilizzando numeri interi a 32 bit, 1 << 32 non è definito. Anche lo spostamento da un numero negativo di bit non è definito.

Se siete stanchi di essere oggetto di interi nativi della vostra piattaforma, il pragma use bigint elude perfettamente il problema del tutto:

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

Il trabocco di un intero senza segno in C ha un comportamento definito, si limita semplicemente a "avvolgere". Perl utilizza tradizionalmente sempre (e usa ancora) interi senza segno quando si usano gli operandi bit a bit a meno che non sia in vigore il prgma intero. – chansen

+0

@ThisSuitIsBlackNon, IMHO dovresti rimuovere parte della documentazione non necessaria aggiunta nella tua modifica, il primo paragrafo escludendo l'ultima frase con un puntatore a perldoc sarebbe sufficiente. – chansen

1

sfida Fun!

use Devel::Peek qw[Dump]; 

for my $n (8, 16, 32, 64) { 
    Dump(~(((1 << ($n - 1)) << 1) - 1)^~0); 
} 

uscita:

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 compilato con:

ivtype='long', ivsize=8, nvtype='double', nvsize=8 
+0

Bello.Fallisce per '$ n = 0', ma questa è un'eccezione facile da verificare. – kjo

+0

@ThisSuitIsBlackNot, Grazie per la modifica, sono davvero ridondanti! – chansen

+0

@kjo, quanti dati puoi memorizzare se hai zero bit? – chansen

2
use Config qw(%Config); 

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

Tecnicamente, la dimensione della parola viene cercata, non calcolato.

Problemi correlati