Qual è l'opzione migliore da usare per dividere un numero intero per 2?


406

Quale delle seguenti tecniche è l'opzione migliore per dividere un numero intero per 2 e perché?

Tecnica 1:

x = x >> 1;

Tecnica 2:

x = x / 2;

Ecco xun numero intero.


75
Se vuoi davvero assegnare di xnuovo il risultato , nessuno dei due è appropriato in questo modo: dovrebbe essere uno x >>= 1o x /= 2, a seconda di ciò che intendi esprimere con l'operazione. Non perché sia ​​più veloce (qualsiasi compilatore moderno compilerà comunque tutte le varianti equivalenti in assembly identici e veloci) ma perché è meno confuso.
circa

33
Non sono d'accordo con leftaroundabout. - Ma ritengo degno di nota il fatto che esiste un'operazione chiamata spostamento aritmetico in molti linguaggi di programmazione che mantiene in posizione il bit di segno e quindi funziona per i valori di segno come previsto. La sintassi potrebbe essere simile x = x >>> 1. Si noti inoltre che, a seconda della piattaforma e del compilatore, può essere abbastanza ragionevole ottimizzare manualmente le divisioni e le moltiplicazioni utilizzando i turni. - Pensando ai microcontroller, ad esempio, senza supporto ALU diretto per la moltiplicazione.
JimmyB,

36
Preferisco x /= 2perché x >>= 1assomiglia troppo al legame monadico;)
fredoverflow

19
@leftaroundabout - Ritengo sia molto più leggibile scrivere x = x / 2invece che x /= 2. Preferenza soggettiva forse :)
JimmyB,

8
@HannoBinder: sicuramente soggettivo, in particolare molta abitudine. IMO, in una lingua in cui tutti gli operatori aritmetici hanno le ⬜=combinazioni, queste dovrebbero essere usate ogni volta che è possibile. Elimina il rumore e pone l'accento sul fatto che xviene modificato , mentre l' =operatore generale suggerisce piuttosto che assume un valore completamente nuovo indipendentemente da quello precedente. - Sempre evitando gli operatori combinati (in modo che sia leggibile in modo da qualcuno che conosce solo gli operatori matematici) può avere il suo punto pure, ma poi avresti bisogno di abbandonare l'estremamente utile ++, --, +=, anche.
lasciato

Risposte:


847

Utilizzare l'operazione che meglio descrive ciò che si sta tentando di fare.

  • Se stai trattando il numero come una sequenza di bit, usa bitshift.
  • Se lo stai trattando come un valore numerico, usa la divisione.

Si noti che non sono esattamente equivalenti. Possono dare risultati diversi per numeri interi negativi. Per esempio:

-5 / 2  = -2
-5 >> 1 = -3

(Ideone)


20
La domanda originale era anche vaga sul termine "migliore". "Migliore" in termini di velocità, leggibilità, domande d'esame per ingannare gli studenti, ecc. In assenza di una spiegazione del significato di "migliore", questa sembra essere la risposta più corretta.
Ray,

47
In C ++ 03, entrambi sono definiti per l'implementazione per numeri negativi e potrebbero fornire gli stessi risultati. In C ++ 11, la divisione è ben definita per i numeri negativi, ma lo spostamento è ancora definito dall'implementazione.
James Kanze,

2
Mentre la definizione di / è implementazione (se arrotondata per eccesso o per difetto per i numeri negativi) definita nei primi standard C. Deve essere sempre coerente con% (operatore modulo / resto).
ctrl-alt-delor,

7
"Implementazione definita" significa che l'implementatore del compilatore deve scegliere tra diverse scelte di implementazione, generalmente con vincoli sostanziali. Qui, un vincolo è che gli operatori %e /devono essere coerenti per entrambi gli operandi positivi e negativi, quindi ciò (a/b)*b+(a%b)==aè vero indipendentemente dai segni di ae b. Di solito, l'autore farà delle scelte per ottenere le migliori prestazioni possibili dalla CPU.
RBerteig,

6
Quindi tutti quelli che dicono "il compilatore lo convertirà comunque in un turno" hanno torto, giusto? A meno che il compilatore non possa garantire di avere a che fare con un numero intero non negativo (o è una costante o è un int senza segno), non può cambiarlo in uno spostamento
Kip

225

Il primo sembra dividere? No. Se vuoi dividere, usa x / 2. Il compilatore può ottimizzarlo per usare bit-shift se possibile (si chiama riduzione della forza), il che lo rende una inutile micro-ottimizzazione se lo fai da solo.


15
Molti compilatori non trasformeranno la divisione per potenza di due in un bit-shift. Sarebbe un'ottimizzazione errata per gli interi con segno. Dovresti provare a guardare l'output dell'assembly dal tuo compilatore e vedere di persona.
exDM69,

1
IIRC L'ho usato per rendere più veloce la riduzione parallela su CUDA (evitare div interi). Comunque sia più di un anno fa, mi chiedo quanto siano intelligenti i compilatori CUDA al giorno d'oggi.
Nils,

9
@ exDM69: molti compilatori lo faranno anche per numeri interi con segno e li regoleranno semplicemente in base al segno. Un ottimo strumento per giocare con queste cose è questo: tinyurl.com/6uww253
PlasmaHH

19
@ exDM69: E questo è pertinente, come? Ho detto "se possibile", non "sempre". Se l'ottimizzazione non è corretta, farlo manualmente non lo rende corretto (in più, come accennato, GCC è abbastanza intelligente da capire la corretta sostituzione per numeri interi con segno).
Cat Plus Plus,

4
Guardando la pagina WikiPedia, questo è apparentemente controverso, ma non lo definirei una riduzione della forza. Una riduzione della forza è quando, in un ciclo, si riduce, ad esempio, dalla moltiplicazione all'aggiunta, aggiungendo ai valori precedenti nel ciclo. Questa è più un'ottimizzazione dello spioncino, che i compilatori possono fare in modo abbastanza affidabile.
SomeCallMeTim

189

Per accatastarsi: ci sono molti motivi per preferire l'utilizzo x = x / 2; Eccone alcuni:

  • esprime il tuo intento in modo più chiaro (supponendo che tu non abbia a che fare con bit di registro o qualcosa del genere)

  • il compilatore lo ridurrà comunque a un'operazione di spostamento

  • anche se il compilatore non lo ha ridotto e ha scelto un'operazione più lenta rispetto allo spostamento, la probabilità che ciò influisca in modo misurabile sulle prestazioni del programma è di per sé piccola (e se influisce in modo misurabile, allora si ha un reale motivo per usare un turno)

  • se la divisione farà parte di un'espressione più ampia, è più probabile che tu ottenga la precedenza giusta se usi l'operatore di divisione:

    x = x / 2 + 5;
    x = x >> 1 + 5;  // not the same as above
  • l'aritmetica firmata potrebbe complicare le cose anche più del problema di precedenza sopra menzionato

  • per ripetere: il compilatore lo farà già per te comunque. In effetti, convertirà la divisione di una costante in una serie di turni, aggiunte e moltiplicazioni per tutti i tipi di numeri, non solo per i poteri di due. Vedi questa domanda per i collegamenti a ulteriori informazioni al riguardo.

In breve, non si acquista nulla codificando un turno quando si intende realmente moltiplicare o dividere, tranne forse una maggiore possibilità di introdurre un bug. È stata una vita da quando i compilatori non sono stati abbastanza intelligenti da ottimizzare questo tipo di cose su un turno, quando appropriato.


5
Vale anche la pena aggiungere che mentre ci sono regole di precedenza, non c'è niente di male nell'usare le parentesi. Durante il rinnovo del codice di produzione, in realtà ho visto qualcosa del modulo a/b/c*d(in cui sono a..dindicate variabili numeriche) anziché molto più leggibile (a*d)/(b*c).

1
Le prestazioni e le ottimizzazioni dipendono dal compilatore e dalla destinazione. Ad esempio, faccio un po 'di lavoro per un microcontrollore in cui qualcosa di superiore a -O0 è disabilitato a meno che non si acquisti il ​​compilatore commerciale, quindi il compilatore non trasformerà sicuramente la divisione in bit-shift. Inoltre, i bitshift richiedono un ciclo e la divisione richiede 18 cicli su questo obiettivo e poiché la velocità di clock dei microcontrollori è piuttosto bassa, questo potrebbe essere effettivamente un notevole risultato prestazionale (ma dipende dal tuo codice - dovresti assolutamente usare / fino a quando la profilazione non ti dice è un problema!)

4
@JackManey, se c'è qualche possibilità che a*do b*cproduca un overflow, la forma meno leggibile non è equivalente e ha un evidente vantaggio. PS Sono d'accordo che le parentesi sono il tuo migliore amico.
Mark Ransom,

@MarkRansom - Un punto giusto (anche se mi sono imbattuto a/b/c*dnel codice R - in un contesto in cui l'overflow significherebbe che qualcosa non andava bene con i dati - e non, per esempio, in un blocco critico di prestazioni del codice C).

Il codice x=x/2;è solo "più chiaro" di x>>=1se xnon ci sarà mai un numero dispari negativo o se uno non si preoccupa degli errori off-by-one. Altrimenti x=x/2;e x>>=1;hanno significati diversi. Se si ha bisogno è il valore calcolato dal x>>=1, avrei considerarlo chiaro rispetto x = (x & ~1)/2o x = (x < 0) ? (x-1)/2 : x/2, o qualsiasi altra formulazione posso pensare di utilizzare divisione per due. Allo stesso modo se uno ha bisogno del valore calcolato da x/=2, è più chiaro di ((x + ((unsigned)x>>31)>>1).
supercat

62

Qual è l'opzione migliore e perché dividere il numero intero per 2?

Dipende da cosa intendi per meglio .

Se vuoi che i tuoi colleghi ti odino o rendano difficile la lettura del tuo codice, sceglierei sicuramente la prima opzione.

Se vuoi dividere un numero per 2, vai con il secondo.

I due non sono equivalenti, non si comportano allo stesso modo se il numero è negativo o all'interno di espressioni più grandi: lo spostamento dei bit ha una precedenza più bassa di +o -, la divisione ha la precedenza più alta.

Dovresti scrivere il tuo codice per esprimere qual è il suo intento. Se le prestazioni sono la tua preoccupazione, non ti preoccupare, l'ottimizzatore fa un buon lavoro in questo tipo di micro-ottimizzazioni.


58

Basta usare divide ( /), presumendo che sia più chiaro. Il compilatore ottimizzerà di conseguenza.


34
Il compilatore dovrebbe ottimizzare di conseguenza.
Noctis Skytower,

12
Se il compilatore non ottimizza di conseguenza, è necessario utilizzare un compilatore migliore.
David Stone,

3
@DavidStone: su quali processori un compilatore può ottimizzare la divisione di un intero con segno eventualmente negativo per qualsiasi costante diversa da 1 per essere efficiente come uno spostamento?
supercat

1
@supercat: questo è un buon punto. Ovviamente potresti archiviare il valore in un numero intero senza segno (che ritengo abbia una reputazione molto peggiore di quanto dovrebbero quando combinato con avvisi di disallineamento firmati / non firmati), e la maggior parte dei compilatori ha anche un modo di dire loro di assumere qualcosa di vero durante l'ottimizzazione . Preferirei racchiuderlo in una macro di compatibilità e avere qualcosa di simile a ASSUME(x >= 0); x /= 2;over x >>= 1;, ma questo è ancora un punto importante da evidenziare.
David Stone,

39

Concordo con altre risposte che dovresti favorire x / 2perché il suo intento è più chiaro e il compilatore dovrebbe ottimizzarlo per te.

Tuttavia, un altro motivo per preferire x / 2rispetto x >> 1è che il comportamento di >>dipende dall'implementazione se xè un segno inted è negativo.

Dalla sezione 6.5.7, punto 5 della norma ISO C99:

Il risultato E1 >> E2è posizioni di bit E1spostate E2a destra . Se E1ha un tipo senza segno o se E1ha un tipo con segno e un valore non negativo, il valore del risultato è la parte integrante del quoziente di E1/ 2 E2. Se E1ha un tipo con segno e un valore negativo, il valore risultante è definito dall'implementazione.


3
Vale la pena notare che il comportamento che molte implementazioni definiscono per i x>>scalepowernumeri negativi sarà esattamente ciò che è necessario quando si divide un valore per una potenza di due per scopi come il rendering dello schermo, mentre l'uso x/scalefactorsarà sbagliato a meno che non si applichino correzioni a valori negativi.
supercat

32

x / 2è più chiaro e x >> 1non è molto più veloce (secondo un micro-benchmark, circa il 30% più veloce per una JVM Java). Come altri hanno notato, per i numeri negativi l'arrotondamento è leggermente diverso, quindi è necessario considerare questo quando si desidera elaborare i numeri negativi. Alcuni compilatori possono convertirsi automaticamente x / 2in x >> 1se sanno che il numero non può essere negativo (anche se non potevo verificarlo).

Anche x / 2non è possibile utilizzare l'istruzione CPU (lenta) di divisione, perché alcune scorciatoie sono possibili , ma è ancora più lenta di x >> 1.

(Questo è un C / C ++ domanda, altri linguaggi di programmazione hanno più operatori. Per Java v'è anche lo spostamento a destra senza segno, x >>> 1che è ancora diverso. Permette di calcolare correttamente il valore medio (media) di due valori, in modo che (a + b) >>> 1la volontà restituisce il valore medio anche per valori molto grandi di ae b. Ciò è necessario ad esempio per la ricerca binaria se gli indici dell'array possono diventare molto grandi. C'era un bug in molte versioni della ricerca binaria , perché avevano usato (a + b) / 2per calcolare la media. non funziona correttamente. La soluzione corretta è quella di utilizzare (a + b) >>> 1invece.)


1
I compilatori non possono convertire x/2per x>>1i casi in cui xpuò essere negativa. Se ciò che si desidera è il valore che x>>1si calcola, sarà quasi sicuramente più veloce di qualsiasi espressione x/2che comporti che calcoli lo stesso valore.
supercat

Hai ragione. A compilatori possono convertono solo x/2a x>>1se sa il valore non è negativo. Proverò ad aggiornare la mia risposta.
Thomas Mueller,

i compilatori evitano comunque divun'istruzione, convertendosi x/2in (x + (x<0?1:0)) >> 1(dove >> è uno spostamento aritmetico di destra, che si sposta in bit di segno). Questo richiede 4 istruzioni: copia il valore, shr (per ottenere solo il bit del segno in un registro), aggiungi, sar. goo.gl/4F8Ms4
Peter Cordes,

La domanda è taggata come C e C ++.
Josh Sanford,

22

Knuth ha detto:

L'ottimizzazione prematura è la radice di tutti i mali.

Quindi suggerisco di usare x /= 2;

In questo modo il codice è facile da capire e penso anche che l'ottimizzazione di questa operazione in quella forma, non significhi una grande differenza per il processore.


4
Quale considereresti il ​​metodo preferito per ridimensionare un numero di una potenza di due se si desidera che i numeri interi sostengano l'assioma (che si applica ai numeri naturali e ai numeri reali) che (n + d) / d = (n / d) + 1? Le violazioni dell'assioma durante il ridimensionamento della grafica causeranno "cuciture" visibili nel risultato. Se si desidera qualcosa che sia uniforme e quasi simmetrico rispetto allo zero, (n+8)>>4funziona bene. Potete offrire un approccio che sia chiaro o efficiente senza utilizzare un turno corretto firmato?
supercat

19

Dai un'occhiata all'output del compilatore per aiutarti a decidere. Ho eseguito questo test su x86-64 con
gcc (GCC) 4.2.1 20070719 [FreeBSD]

Vedi anche le uscite del compilatore online su godbolt .

Quello che vedi è che il compilatore usa sarlun'istruzione (spostamento aritmetico a destra) in entrambi i casi, quindi riconosce la somiglianza tra le due espressioni. Se si utilizza la divisione, il compilatore deve anche regolare i numeri negativi. Per fare ciò sposta il bit di segno verso il basso al bit di ordine più basso e lo aggiunge al risultato. Questo risolve il problema off-by-one quando si spostano i numeri negativi, rispetto a ciò che farebbe una divisione.
Poiché il caso di divisione fa 2 turni, mentre il caso di turno esplicito fa solo uno, ora possiamo spiegare alcune delle differenze di prestazione misurate da altre risposte qui.

Codice C con uscita assembly:

Per dividere, il tuo input sarebbe

int div2signed(int a) {
  return a / 2;
}

e questo si compila a

    movl    %edi, %eax
    shrl    $31, %eax
    addl    %edi, %eax
    sarl    %eax
    ret

allo stesso modo per il turno

int shr2signed(int a) {
  return a >> 1;
}

con uscita:

    sarl    %edi
    movl    %edi, %eax
    ret

A seconda di ciò che si sta facendo, potrebbe correggere l'errore off-by-one o potrebbe causare un errore off-by-one (rispetto a ciò che è effettivamente necessario) che richiederà l'uso di ulteriore codice per risolverlo. Se ciò che si desidera è un risultato preciso, uno spostamento a destra è più veloce e più facile di qualsiasi alternativa io conosca.
supercat

Se hai bisogno di un piano, è improbabile che descrivi ciò che vuoi come "dividendo per 2"
Michael Donohue,

La divisione sia dei numeri naturali che dei numeri reali sostiene l'assioma che (n + d) / d = (n / d) +1. Anche la divisione dei numeri reali sostiene (-n) / d = - (n / d), un assioma privo di significato con i numeri naturali. Non è possibile avere un operatore di divisione che è chiuso su numeri interi e sostiene entrambi gli assiomi. A mio avviso, dire che il primo assioma dovrebbe valere per tutti i numeri e il secondo solo per i reali sembra più naturale del dire che il primo dovrebbe valere per numeri interi o reali ma non per numeri interi. Inoltre, sono curioso di sapere in quali casi il secondo assioma è effettivamente utile .
supercat

1
Un metodo di divisione intera che soddisfa il primo assioma suddividerà la linea numerica in regioni di dimensioni d. Tale partizionamento è utile per vari scopi. Anche se si preferisce avere il punto di interruzione in un punto diverso da 0 a -1, l'aggiunta di un offset lo sposta. Una divisione intera che soddisfa il secondo assioma suddividerà la linea numerica in regioni che sono per lo più di dimensioni d, ma una delle quali è di dimensioni 2*d-1. Divisioni non esattamente "uguali". Puoi offrire suggerimenti su quando la partizione oddball è effettivamente utile?
supercat

L'output del compilatore per shr2signed è errato. gcc su x86 sceglie di implementare >> di numeri interi con segno con turni aritmetici ( sar).goo.gl/KRgIkb . Questo post della mailing list ( gcc.gnu.org/ml/gcc/2000-04/msg00152.html ) conferma che gcc utilizza storicamente turni aritmetici per gli integri firmati, quindi è altamente improbabile che FreeBSD gcc 4.2.1 abbia usato turni senza segno. Ho aggiornato il tuo post per risolvere il problema e il primo paragrafo che diceva entrambi usato shr, quando in realtà è SAR che entrambi usano. SHR è il modo in cui estrae il bit di segno per il /caso. Incluso anche un link Godbolt.
Peter Cordes,

15

Solo una nota aggiunta -

x * = 0,5 sarà spesso più veloce in alcuni linguaggi basati su VM, in particolare ActionScript, poiché la variabile non dovrà essere controllata per dividere per 0.


2
@minitech: è una brutta prova. Tutto il codice nel test è costante. Prima ancora che il codice sia JIT, eliminerà tutte le costanti.

@ M28: ero abbastanza sicuro che gli interni di jsPerf (cioè eval) facessero succedere di nuovo ogni volta. Indipendentemente da ciò, sì, è un test piuttosto negativo, perché è un'ottimizzazione molto sciocca.
Ry-

13

Usa x = x / 2; O x /= 2;Perché è possibile che un nuovo programmatore ci lavori in futuro. Quindi sarà più facile per lui scoprire cosa sta succedendo nella riga di codice. Tutti potrebbero non essere a conoscenza di tali ottimizzazioni.


12

Sto raccontando ai fini della programmazione delle competizioni. Generalmente hanno input molto grandi in cui la divisione per 2 ha luogo molte volte e si sa che l'input è positivo o negativo.

x >> 1 sarà migliore di x / 2. Ho controllato su ideone.com eseguendo un programma in cui sono avvenute più di 10 ^ 10 divisioni per 2 operazioni. x / 2 ha impiegato quasi 5,5 secondi mentre x >> 1 ha impiegato quasi 2,6 secondi per lo stesso programma.


1
Per valori senza segno, un compilatore dovrebbe ottimizzare x/2al x>>1. Per i valori con segno, quasi tutte le implementazioni definiscono x>>1un significato equivalente x/2ma che può essere calcolato più rapidamente quando xè positivo ed è utilmente diverso da x/2quando xè negativo.
supercat

12

Direi che ci sono diverse cose da considerare.

  1. Bitshift dovrebbe essere più veloce, poiché non è davvero necessario un calcolo speciale per spostare i bit, tuttavia, come sottolineato, ci sono potenziali problemi con numeri negativi. Se hai la certezza di avere numeri positivi e stai cercando velocità, consiglierei bitshift.

  2. L'operatore di divisione è molto facile da leggere per gli umani. Quindi, se stai cercando la leggibilità del codice, potresti usarlo. Si noti che il campo dell'ottimizzazione del compilatore ha fatto molta strada, quindi rendere il codice facile da leggere e comprendere è una buona pratica.

  3. A seconda dell'hardware sottostante, le operazioni possono avere velocità diverse. La legge di Amdal è di accelerare il caso comune. Quindi potresti avere hardware in grado di eseguire diverse operazioni più velocemente di altri. Ad esempio, moltiplicare per 0,5 potrebbe essere più veloce della divisione per 2. (Concesso, potrebbe essere necessario prendere il piano della moltiplicazione se si desidera applicare la divisione intera).

Se stai cercando prestazioni allo stato puro, consiglierei di creare alcuni test che potrebbero eseguire le operazioni milioni di volte. Prova l'esecuzione più volte (la dimensione del tuo campione) per determinare quale sia statisticamente migliore con il tuo sistema operativo / hardware / compilatore / codice.


2
"Bitshift dovrebbe essere più veloce". i compilatori ottimizzeranno le divisioni in bit
Trevor Hickey

Spero che lo facciano, ma se non si ha accesso alla fonte del compilatore, non si può essere sicuri :)
James Oravec

1
Consiglierei anche bitshift se la propria implementazione lo gestisce nel modo più comune e il modo in cui si desidera gestire i numeri negativi corrisponde a ciò che >>fa e non corrisponde a ciò che /fa.
supercat

12

Per quanto riguarda la CPU, le operazioni bit-shift sono più veloci delle operazioni di divisione. Tuttavia, il compilatore lo sa e si ottimizzerà in modo adeguato nella misura del possibile, in modo da poter programmare nel modo più logico e tranquillo sapendo che il codice funziona in modo efficiente. Ma ricorda che una unsigned intlattina (in alcuni casi) può essere ottimizzata meglio di una intper ragioni precedentemente evidenziate. Se non hai bisogno di un segno aritmico, non includere il bit di segno.


11

x = x / 2; è il codice adatto da usare .. ma un'operazione dipende dal proprio programma su come si desidera produrre l'output.


11

Rendi le tue intenzioni più chiare ... per esempio, se vuoi dividere, usa x / 2 e lascia che il compilatore lo ottimizzi per spostare l'operatore (o qualsiasi altra cosa).

I processori di oggi non permetteranno che queste ottimizzazioni abbiano alcun impatto sulle prestazioni dei tuoi programmi.


10

La risposta a questo dipenderà dall'ambiente in cui lavori.

  • Se stai lavorando su un microcontrollore a 8 bit o altro senza supporto hardware per la moltiplicazione, lo spostamento dei bit è previsto e banale e mentre il compilatore si trasformerà quasi sicuramente x /= 2in x >>= 1, la presenza di un simbolo di divisione innalzerà più sopracciglia in quell'ambiente rispetto a usando uno spostamento per effettuare una divisione.
  • Se stai lavorando in un ambiente critico o in una sezione di codice, o il tuo codice potrebbe essere compilato con l'ottimizzazione del compilatore disattivata, x >>= 1con un commento che spiega che il suo ragionamento è probabilmente meglio solo per chiarezza di scopo.
  • Se non sei in una delle condizioni di cui sopra, rendi il tuo codice più leggibile semplicemente usando x /= 2. Meglio salvare il programmatore successivo a cui capita di guardare il tuo codice il doppio di 10 secondi sulla tua operazione di cambio piuttosto che provare inutilmente di sapere che il turno era più efficiente ottimizzazione del compilatore.

Tutti questi assumono numeri interi senza segno. Il semplice spostamento non è probabilmente quello che vuoi per firmato. Inoltre, DanielH sottolinea un buon punto sull'uso x *= 0.5di alcuni linguaggi come ActionScript.


8

mod 2, prova per = 1. non so la sintassi in c. ma questo potrebbe essere il più veloce.


7

generalmente il turno giusto divide:

q = i >> n; is the same as: q = i / 2**n;

questo è talvolta usato per accelerare i programmi a costo di chiarezza. Non penso che dovresti farlo. Il compilatore è abbastanza intelligente da eseguire automaticamente l'accelerazione. Ciò significa che inserirsi in un turno non guadagna nulla a scapito della chiarezza .

Dai un'occhiata a questa pagina dalla Programmazione pratica C ++.


Se si desidera calcolare il valore che, ad esempio, (x+128)>>8si calcola per valori xnon prossimi al massimo, come si può fare concisamente senza uno spostamento? Nota che (x+128)/256non funzionerà. Conosci qualche bella espressione che lo farà?
supercat

7

Ovviamente, se stai scrivendo il tuo codice per il prossimo che lo legge, cerca la chiarezza di "x / 2".

Tuttavia, se la velocità è il tuo obiettivo, provalo in entrambi i modi e cronometra i risultati. Qualche mese fa ho lavorato su una routine di convoluzione bitmap che prevedeva di passare attraverso una matrice di numeri interi e di dividere ogni elemento per 2. Ho fatto tutti i tipi di cose per ottimizzarlo, incluso il vecchio trucco di sostituire "x >> 1" con "x / 2" .

Quando ho cronometrato in entrambi i modi ho scoperto con mia sorpresa che x / 2 era più veloce di x >> 1

Questo utilizzava Microsoft VS2008 C ++ con le ottimizzazioni predefinite attivate.


4

In termini di prestazioni. Le operazioni di spostamento della CPU sono significativamente più veloci rispetto alla divisione dei codici operativi. Pertanto, dividendo per due o moltiplicando per 2 ecc. Tutti beneficiano delle operazioni a turni.

Per quanto riguarda l'aspetto grafico. Come ingegneri quando siamo diventati così attaccati ai cosmetici che nemmeno le belle donne lo usano! :)


3

X / Y è un operatore di spostamento corretto ... e ">>" ... se vogliamo due dividere un numero intero possiamo usare l'operatore di dividendo (/). l'operatore shift viene utilizzato per spostare i bit ..

x = x / 2; x / = 2; possiamo usare così ..


0

Mentre x >> 1 è più veloce di x / 2, l'uso corretto di >> quando si ha a che fare con valori negativi è un po 'più complicato. Richiede qualcosa di simile al seguente:

// Extension Method
public static class Global {
    public static int ShiftDivBy2(this int x) {
        return (x < 0 ? x + 1 : x) >> 1;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.