2012-12-09 15 views
23

Capisco che lo >>> corregge l'overflow: quando si aggiungono due lunghi positivi, è possibile che si ottenga un numero negativo. Qualcuno può spiegare come questo spostamento bit-a-bit risolva magicamente il problema dell'overflow? E in che modo è diverso da >>?Perché in Java (alto + basso)/2 è sbagliato ma (alto + basso) >>> 1 non è?


mio sospetto: Io penso che abbia a che fare con il fatto che Java utilizza due complimenti così l'overflow è il numero giusto se avessimo lo spazio in più, ma perché non lo facciamo diventa negativo. Quindi, quando si sposta e si paga con lo zero, magicamente viene corretto a causa dei due complimenti. Ma posso sbagliarmi e qualcuno con un cervello bit a bit deve confermare. :)

+1

Presumo che tu ti stia riferendo a [il bug di ricerca binaria] (http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html) ? – Mehrdad

+1

Come sai che risolve il problema di overflow? Inoltre, quale problema di overflow? –

+1

Joshua Bloch mi ha detto: http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html – JohnPristine

risposta

46

In breve, (high + low) >>> 1 è un trucco che utilizza il segno-bit non utilizzati per eseguire una corretta media di numeri non negativi.


Partendo dal presupposto che high e low sono entrambi non negativo, sappiamo per certo che la parte superiore del bit più (il segno-bit) è pari a zero.

Quindi sia high sia low sono in realtà numeri interi a 31 bit.

high = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824 
low = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824 

Quando li si sommano possono "rovesciarsi" nel bit superiore.

high + low =  1000 0000 0000 0000 0000 0000 0000 0000 
      = 2147483648 as unsigned 32-bit integer 
      = -2147483648 as signed 32-bit integer 

(high + low)/2 = 1100 0000 0000 0000 0000 0000 0000 0000 = -1073741824 
(high + low) >>> 1 = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824 
  • Come un intero a 32 bit con segno, è troppo pieno e ribalta negativo. Pertanto (high + low)/2 è errato perché high + low potrebbe essere negativo.

  • Come interi a 32 bit senza segno, la somma è corretta. Tutto ciò che serve è quello di dividerlo per 2.

Naturalmente Java non supporta interi senza segno, quindi la cosa migliore che dobbiamo dividere per 2 (come intero senza segno) è la logica destra-shift >>> .

Nelle lingue con interi senza segno (come C e C++), diventa più complicato dal momento che l'input può essere pieno di interi a 32 bit. Una soluzione è: low + ((high - low)/2)


fine di enumerare le differenze tra >>>, >> e /:

  • >>> è logico shift destro. Riempie i bit superiori con zero.
  • >> è aritmetico a destra. Riempie la parte superiore con copie del primo bit originale.
  • / è divisione.

Matematicamente:

  • x >>> 1 tratta x come intero senza segno e lo divide per due. Arrotonda.
  • x >> 1 tratta x come numero intero con segno e lo divide per due. Arrotonda all'infinito negativo.
  • x/2 tratta x come numero intero con segno e lo divide per due. Arrotonda verso lo zero.
+0

Non sicuro quando si desidera utilizzare >>> e quando si desidera utilizzare >>. Penso che in quel caso abbiamo scelto >>> di correggere un possibile problema di overflow (sign bit spill). Qual è la regola generale per scegliere tra >> e >>>? – JohnPristine

+10

'>>>' è logico a destra. Riempie i bit superiori con zero. '>>' è il giusto calcolo aritmetico. Riempie i bit in alto con copie del primo bit originale. Matematicamente, '>>> 1' tratta il numero come non firmato e divide per due arrotondamenti. '>> 1' tratta il numero come firmato e arrotonda verso l'infinito negativo. '/ 2' tratta il numero come firmato e arrotonda verso zero. – Mysticial

10

Riempie a zero i bit più in alto invece di riempirli con il segno.

int a = 0x40000000; 
(a + a)/2 == 0xC0000000; 
(a + a) >>> 1 == 0x40000000; 
+0

Vedi la mia modifica alla domanda ... – JohnPristine

+1

@JohnPristine: Bene , una CPU a complemento a 2 esegue l'aggiunta esattamente nello stesso modo per interi firmati e non firmati ... l'unica differenza è in '/ 2'. Quindi fino a quel punto, tutto è corretto. E ovviamente, dato che ci sono abbastanza bit per rappresentare la somma, ci sono abbastanza bit per rappresentare il quoziente, e '>>> 1' fa proprio questo. L'unico motivo '/ 2' è sbagliato è che cerca di correggere il segno, e ovviamente' >>> 1' esegue uno spostamento a destra bit a bit, che è lo stesso della divisione senza segno di 2 ... quindi * deve * funziona correttamente Non sono sicuro che questo abbia risposto alla tua domanda ... – Mehrdad

+0

La mia affermazione è corretta: "Penso che abbia a che fare con il fatto che Java usi due complimenti quindi l'overflow è il numero giusto se abbiamo lo spazio extra ma perché non diventiamo negativi, quindi quando si cambia e si paga con lo zero si rimette magicamente "? – JohnPristine

Problemi correlati