Quelle est la meilleure option à utiliser pour diviser un nombre entier par 2?

406

Laquelle des techniques suivantes est la meilleure option pour diviser un entier par 2 et pourquoi?

Technique 1:

x = x >> 1;

Technique 2:

x = x / 2;

Voici xun entier.

Abhineet
la source
75
Si vous voulez vraiment attribuer à xnouveau le résultat , ni l'un ni l'autre n'est approprié de cette façon: il devrait être l'un x >>= 1ou l' autre x /= 2, selon ce que vous avez l'intention d'exprimer avec l'opération. Non pas parce qu'il est plus rapide (tout compilateur moderne compilera toutes les variantes équivalentes à un assemblage identique et rapide de toute façon) mais parce qu'il est moins déroutant.
gauche autour
33
Je ne suis pas d'accord avec la gauche sur. - Mais je pense qu'il est à noter qu'il existe une opération appelée décalage arithmétique dans de nombreux langages de programmation qui maintient le bit de signe en place et fonctionne donc pour les valeurs signées comme prévu. La syntaxe peut être similaire x = x >>> 1. Notez également qu'en fonction de la plate-forme et du compilateur, il peut être tout à fait raisonnable d'optimiser manuellement les divisions et les multiplications à l'aide de décalages. - Penser aux microcontrôleurs, par exemple, sans prise en charge directe ALU pour la multiplication.
JimmyB
36
Je préfère x /= 2parce que x >>= 1ça ressemble trop à une reliure monadique;)
fredoverflow
19
@leftaroundabout - Je pense simplement qu'il est beaucoup plus lisible d'écrire x = x / 2au lieu de x /= 2. Préférence subjective peut-être :)
JimmyB
8
@HannoBinder: certainement subjectif, en particulier beaucoup d'habitude. OMI, dans un langage où tous les opérateurs arithmétiques ont les ⬜=combinaisons, celles-ci doivent être utilisées chaque fois que cela est possible. Il supprime le bruit et met l'accent sur le fait qu'il xest modifié , tandis que l' =opérateur général suggère plutôt qu'il prenne une valeur complètement nouvelle indépendante de l'ancienne. - Toujours en évitant les opérateurs combinés (pour qu'il soit lisible que quelqu'un qui ne connaît que les opérateurs mathématiques) peut avoir son point aussi, mais vous auriez besoin de renoncer à la très utile ++, --, +=aussi.
gauche autour

Réponses:

847

Utilisez l'opération qui décrit le mieux ce que vous essayez de faire.

  • Si vous traitez le nombre comme une séquence de bits, utilisez bitshift.
  • Si vous la traitez comme une valeur numérique, utilisez la division.

Notez qu'ils ne sont pas exactement équivalents. Ils peuvent donner des résultats différents pour les entiers négatifs. Par exemple:

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

(ideone)

Mark Byers
la source
20
La question initiale était également vague à propos du terme «meilleur». «Meilleur» en termes de vitesse, de lisibilité, de question d'examen pour tromper les étudiants, etc. En l'absence d'explication de ce que signifie «meilleur», cela semble être la réponse la plus correcte.
Ray
47
En C ++ 03, les deux sont définis par l'implémentation pour des nombres négatifs et peuvent donner les mêmes résultats. En C ++ 11, la division est bien définie pour les nombres négatifs, mais le décalage est toujours défini par l'implémentation.
James Kanze
2
Alors que la définition de / est mise en œuvre (si elle est arrondie vers le haut ou vers le bas pour les nombres négatifs) définie dans les premières normes C. Il doit toujours être cohérent avec% (opérateur modulo / reste).
ctrl-alt-delor
7
«Implémentation définie» signifie que l'implémenteur du compilateur a pu choisir parmi plusieurs choix d'implémentation, généralement avec des contraintes importantes. Ici, une contrainte est que les opérateurs %et /doivent être cohérents pour les opérandes positifs et négatifs, ce qui (a/b)*b+(a%b)==aest vrai quels que soient les signes de aet b. Habituellement, l'auteur fera des choix qui obtiendront les meilleures performances possibles du CPU.
RBerteig
6
Donc, tous ceux qui disent que "le compilateur le convertira de toute façon en décalage" ont tort, non? À moins que le compilateur ne puisse garantir que vous avez affaire à un entier non négatif (que ce soit une constante ou un entier non signé), il ne peut pas le changer en décalage
Kip
225

La première ressemble-t-elle à une division? Non. Si vous voulez diviser, utilisez x / 2. Le compilateur peut l'optimiser pour utiliser le bit-shift si possible (c'est ce qu'on appelle la réduction de la force), ce qui en fait une micro-optimisation inutile si vous le faites vous-même.

Cat Plus Plus
la source
15
De nombreux compilateurs ne transformeront pas la division par la puissance de deux en décalage de bits. Ce serait une optimisation incorrecte pour les entiers signés. Vous devriez essayer de regarder la sortie d'assembly de votre compilateur et de voir par vous-même.
exDM69
1
IIRC I a utilisé cela pour accélérer la réduction parallèle sur CUDA (éviter les div entiers). Cependant, c'était il y a plus d'un an, je me demande à quel point les compilateurs CUDA sont intelligents de nos jours.
Nils
9
@ exDM69: De nombreux compilateurs le feront même pour les entiers signés, et les ajusteront simplement en fonction de la signature. Un bon outil pour jouer avec ces choses est le suivant: tinyurl.com/6uww253
PlasmaHH
19
@ exDM69: Et c'est pertinent, comment? J'ai dit "si possible", pas "toujours". Si l'optimisation est incorrecte, le faire manuellement ne le rend pas correct (plus comme mentionné, GCC est assez intelligent pour trouver un remplacement approprié pour les entiers signés).
Cat Plus Plus
4
En regardant la page WikiPedia, c'est apparemment controversé, mais je n'appellerais pas cela une réduction de la force. Une réduction de la force est lorsque, dans une boucle, vous réduisez de, par exemple, la multiplication à l'addition, en ajoutant aux valeurs précédentes de la boucle. Il s'agit plus d'une optimisation de judas, que les compilateurs peuvent faire de manière assez fiable.
SomeCallMeTim
189

Pour empiler: il y a tellement de raisons de favoriser l'utilisation x = x / 2; Voici quelques-unes:

  • il exprime plus clairement votre intention (en supposant que vous n'avez pas affaire à des bits de registre de torsion de bits ou à quelque chose)

  • le compilateur le réduira de toute façon à une opération de décalage

  • même si le compilateur ne l'a pas réduit et a choisi une opération plus lente que le changement, la probabilité que cela finisse par affecter les performances de votre programme de manière mesurable est elle-même extrêmement faible (et si elle l'affecte de manière mesurable, alors vous avez un réel raison d'utiliser un quart de travail)

  • si la division va faire partie d'une expression plus large, il est plus probable que vous ayez la priorité si vous utilisez l'opérateur de division:

    x = x / 2 + 5;
    x = x >> 1 + 5;  // not the same as above
  • l'arithmétique signée pourrait compliquer les choses encore plus que le problème de priorité mentionné ci-dessus

  • pour réitérer - le compilateur le fera déjà de toute façon. En fait, il convertira la division par une constante en une série de décalages, s'ajoute et se multiplie pour toutes sortes de nombres, pas seulement des puissances de deux. Voir cette question pour des liens vers encore plus d'informations à ce sujet.

En bref, vous n'achetez rien en codant un décalage lorsque vous voulez vraiment multiplier ou diviser, sauf peut-être une possibilité accrue d'introduire un bug. Cela fait toute une vie que les compilateurs n'étaient pas assez intelligents pour optimiser ce genre de chose en un quart de travail le cas échéant.

Michael Burr
la source
5
Il convient également d'ajouter que s'il existe des règles de priorité, il n'y a rien de mal à utiliser des parenthèses. En réorganisant du code de production, j'ai en fait vu quelque chose de la forme a/b/c*d(où a..ddénoté des variables numériques) au lieu de beaucoup plus lisible (a*d)/(b*c).
1
Les performances et les optimisations dépendent du compilateur et de la cible. Par exemple, je travaille pour un microcontrôleur où tout ce qui est supérieur à -O0 est désactivé, sauf si vous achetez le compilateur commercial, de sorte que le compilateur ne transformera certainement pas la division en décalages de bits. De plus, les décalages de bits prennent un cycle et la division prend 18 cycles sur cette cible et comme la vitesse d'horloge des microcontrôleurs est assez faible, cela peut en effet être un impact notable sur les performances (mais cela dépend de votre code - vous devez absolument utiliser / jusqu'à ce que le profilage vous indique c'est un problème!)
4
@JackManey, s'il y a une possibilité a*dou b*cqui produirait un débordement, la forme moins lisible n'est pas équivalente et a un avantage évident. PS Je suis d'accord que les parenthèses sont votre meilleur ami.
Mark Ransom
@MarkRansom - Un bon point (même si je suis tombé sur a/b/c*ddu code R - dans un contexte où un débordement signifierait que quelque chose n'allait vraiment pas avec les données - et non, par exemple, dans un bloc de code C à performances critiques).
Le code x=x/2;est seulement "plus clair" que x>>=1s'il xne sera jamais un nombre négatif impair ou que l'on ne se soucie pas d'erreurs ponctuelles. Sinon x=x/2;et x>>=1;ont des significations différentes. Si ce dont on a besoin est la valeur calculée par x>>=1, je considérerais cela comme plus clair que x = (x & ~1)/2ou x = (x < 0) ? (x-1)/2 : x/2, ou toute autre formulation à laquelle je peux penser d'utiliser la division par deux. De même, si l'on a besoin de la valeur calculée par x/=2, c'est plus clair que ((x + ((unsigned)x>>31)>>1).
supercat
62

Laquelle est la meilleure option et pourquoi diviser le nombre entier par 2?

Cela dépend de ce que vous entendez par le meilleur .

Si vous voulez que vos collègues vous haïssent ou rendent votre code difficile à lire, j'irais certainement avec la première option.

Si vous voulez diviser un nombre par 2, optez pour le second.

Les deux ne sont pas équivalents, ils ne se comportent pas de la même façon si le nombre est négatif ou dans des expressions plus grandes - le décalage de bits a une priorité inférieure à +ou- , la division a une priorité plus élevée.

Vous devez écrire votre code pour exprimer son intention. Si les performances vous préoccupent, ne vous inquiétez pas, l'optimiseur fait du bon travail avec ce type de micro-optimisations.

Luchian Grigore
la source
58

Utilisez simplement divide ( /), en supposant que c'est plus clair. Le compilateur optimisera en conséquence.

Justin
la source
34
Le compilateur doit optimiser en conséquence.
Noctis Skytower
12
Si le compilateur ne optimize en conséquence, vous devez utiliser un meilleur compilateur.
David Stone
3
@DavidStone: Sur quels processeurs un compilateur peut -il optimiser la division d'un entier signé éventuellement négatif par une constante autre que 1 pour être aussi efficace qu'un décalage?
supercat
1
@supercat: C'est un bon point. Vous pouvez bien sûr stocker la valeur dans un entier non signé (qui, selon moi, a une réputation bien pire qu'elle ne le devrait lorsqu'il est combiné avec des avertissements de disparité signés / non signés), et la plupart des compilateurs ont également un moyen de leur dire de supposer que quelque chose est vrai lors de l'optimisation . Je préférerais que l' emballage dans une macro de compatibilité et avoir quelque chose comme ASSUME(x >= 0); x /= 2;plus x >>= 1;, mais qui est encore un point important de mettre en place.
David Stone
39

Je suis d'accord avec d'autres réponses que vous devriez privilégier x / 2car son intention est plus claire et le compilateur devrait l'optimiser pour vous.

Cependant, une autre raison de préférer x / 2plus x >> 1est que le comportement de >>dépend de la mise en œuvre si xest un signé intet est négative.

De la section 6.5.7, puce 5 de la norme ISO C99:

Le résultat E1 >> E2est des positions de bits E1décalées vers la droite E2. Si E1a un type non signé ou si E1a un type signé et une valeur non négative, la valeur du résultat fait partie intégrante du quotient de E1/ 2 E2. Si E1a un type signé et une valeur négative, la valeur résultante est définie par l'implémentation.

jamesdlin
la source
3
Il convient de noter que le comportement que de nombreuses implémentations définissent pour x>>scalepowerles nombres négatifs sera précisément ce qui est nécessaire lors de la division d'une valeur par une puissance de deux à des fins telles que le rendu d'écran, tandis que l'utilisation x/scalefactorsera erronée à moins que l'on applique des corrections aux valeurs négatives.
supercat
32

x / 2est plus clair et x >> 1n'est pas beaucoup plus rapide (selon un micro-benchmark, environ 30% plus rapide pour une JVM Java). Comme d'autres l'ont noté, pour les nombres négatifs, l'arrondi est légèrement différent, vous devez donc en tenir compte lorsque vous souhaitez traiter des nombres négatifs. Certains compilateurs peuvent se convertir automatiquement x / 2enx >> 1 s'ils savent que le nombre ne peut pas être négatif (même pensé que je ne pouvais pas vérifier).

Même x / 2ne peut pas utiliser l'instruction CPU de division (lente), car certains raccourcis sont possibles , mais il est toujours plus lent que x >> 1.

(Ceci est une question C / C ++, d' autres langages de programmation ont plusieurs opérateurs. Pour Java il y a aussi le décalage droit non signé, x >>> 1qui est encore différente. Il permet de calculer correctement la valeur moyenne (moyenne) de deux valeurs, de sorte que la (a + b) >>> 1volonté renvoie la valeur moyenne même pour les très grandes valeurs de aet b. Ceci est requis par exemple pour la recherche binaire si les indices du tableau peuvent devenir très grands. Il y avait un bogue dans de nombreuses versions de la recherche binaire , car elles servaient (a + b) / 2à calculer la moyenne. ne fonctionne pas correctement. La bonne solution est d’utiliser à la (a + b) >>> 1place.)

Thomas Mueller
la source
1
Compilateurs ne peuvent pas convertir x/2à x>>1dans les cas où xpeut être négatif. Si ce que l'on veut, c'est la valeur qui x>>1serait calculée, ce sera presque certainement plus rapide que toute expression impliquant x/2qui calcule la même valeur.
supercat
Tu as raison. Un compilateur ne peut convertir x/2à x>>1s'il connaît la valeur est négative. Je vais essayer de mettre à jour ma réponse.
Thomas Mueller
cependant, les compilateurs évitent toujours une divinstruction, en convertissant x/2en (x + (x<0?1:0)) >> 1(où >> est un décalage arithmétique à droite, qui se décale en bits de signe). Cela prend 4 instructions: copier la valeur, shr (pour obtenir juste le bit de signe dans un reg), ajouter, sar. goo.gl/4F8Ms4
Peter Cordes
La question est étiquetée comme C et C ++.
Josh Sanford
22

Knuth a déclaré:

L'optimisation prématurée est la racine de tout Mal.

Je suggère donc d'utiliser x /= 2;

De cette façon, le code est facile à comprendre et je pense également que l'optimisation de cette opération sous cette forme, ne signifie pas une grande différence pour le processeur.

Ivan Californias
la source
4
Que considéreriez-vous comme la méthode préférée de réduction d'un nombre par une puissance de deux si l'on veut que les entiers maintiennent l'axiome (qui s'applique aux nombres naturels et réels) qui (n + d) / d = (n / d) + 1? Les violations de cet axiome lors de la mise à l'échelle des graphiques entraîneront des "coutures" visibles dans le résultat. Si l'on veut quelque chose qui est uniforme et presque symétrique par rapport à zéro, ça (n+8)>>4marche bien. Pouvez-vous proposer une approche aussi claire ou aussi efficace sans utiliser un décalage à droite signé?
supercat
19

Jetez un œil à la sortie du compilateur pour vous aider à décider. J'ai exécuté ce test sur x86-64 avec
gcc (GCC) 4.2.1 20070719 [FreeBSD]

Voir également les sorties du compilateur en ligne sur godbolt .

Ce que vous voyez, c'est que le compilateur utilise une sarlinstruction (décalage vers la droite arithmétique) dans les deux cas, il reconnaît donc la similitude entre les deux expressions. Si vous utilisez la division, le compilateur doit également ajuster les nombres négatifs. Pour ce faire, il décale le bit de signe vers le bit de poids faible et l'ajoute au résultat. Cela résout le problème du coup par coup lors du déplacement de nombres négatifs, par rapport à ce que ferait une division.
Étant donné que le cas de division fait 2 changements, alors que le cas de changement explicite n'en fait qu'un, nous pouvons maintenant expliquer ici certaines des différences de performances mesurées par d'autres réponses.

Code C avec sortie d'assemblage:

Pour diviser, votre entrée serait

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

et cela compile pour

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

de même pour le décalage

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

avec sortie:

    sarl    %edi
    movl    %edi, %eax
    ret
Michael Donohue
la source
Selon ce que l'on fait, cela peut corriger l'erreur de coup par coup, ou cela peut provoquer une erreur de coup par coup (par rapport à ce qui est réellement nécessaire) qui nécessitera l'utilisation de code supplémentaire pour la corriger. Si ce que l'on veut est un résultat plancher, un décalage à droite est plus rapide et plus facile que toute autre alternative que je connaisse.
supercat
Si vous avez besoin d'un plancher, il est peu probable que vous décriviez ce que vous voulez comme "divisant par 2"
Michael Donohue
La division des nombres naturels et des nombres réels confirme l'axiome que (n + d) / d = (n / d) +1. La division des nombres réels confirme également (-n) / d = - (n / d), un axiome qui n'a pas de sens avec les nombres naturels. Il n'est pas possible d'avoir un opérateur de division qui est fermé sur des entiers et maintient les deux axiomes. À mon avis, dire que le premier axiome devrait être valable pour tous les nombres et le second uniquement pour les réels semble plus naturel que de dire que le premier devrait être valable pour les nombres entiers ou réels mais pas pour les entiers. De plus, je suis curieux de savoir dans quels cas le deuxième axiome est réellement utile .
supercat
1
Une méthode de division entière qui satisfait le premier axiome partitionnera la droite numérique en régions de taille d. Un tel partitionnement est utile à de nombreuses fins. Même si l'on préfère avoir le point d'arrêt ailleurs qu'entre 0 et -1, l'ajout d'un décalage le déplacera. Une division entière qui satisfait le deuxième axiome divisera la droite numérique en régions qui sont principalement de taille d, mais dont l'une est de taille 2*d-1. Pas exactement des divisions "égales". Pouvez-vous suggérer quand la partition Oddball est réellement utile?
supercat
La sortie de votre compilateur pour shr2signed est incorrecte. gcc sur x86 choisit d'implémenter >> d'entiers signés avec des décalages arithmétiques ( sar). goo.gl/KRgIkb . Cette publication sur la liste de diffusion ( gcc.gnu.org/ml/gcc/2000-04/msg00152.html ) confirme que gcc utilise historiquement des décalages arithmétiques pour les entrées signées, il est donc très peu probable que FreeBSD gcc 4.2.1 ait utilisé un décalage non signé. J'ai mis à jour votre message pour corriger cela et le premier paragraphe disant que les deux utilisaient shr, alors que c'est en fait SAR qu'ils utilisent tous les deux. Le SHR est la façon dont il extrait le bit de signe pour le /boîtier. Également inclus un lien Godbolt.
Peter Cordes
15

Juste une note ajoutée -

x * = 0,5 sera souvent plus rapide dans certains langages basés sur des machines virtuelles - notamment actionscript, car la variable ne devra pas être vérifiée pour la division par 0.

ansiart
la source
2
@minitech: C'est un si mauvais test. Tout le code du test est constant. Avant même que le code ne soit JIT, il éliminera toutes les constantes.
@ M28: J'étais à peu près sûr que les éléments internes de jsPerf (c'est-à-dire eval) faisaient en sorte que cela se reproduise à chaque fois. Quoi qu'il en soit, oui, c'est un très mauvais test, car c'est une optimisation très stupide.
Ry-
13

Utilisez x = x / 2; OR, x /= 2;car il est possible qu'un nouveau programmeur y travaille à l'avenir. Il lui sera donc plus facile de savoir ce qui se passe dans la ligne de code. Tout le monde peut ne pas être au courant de telles optimisations.

Imdad
la source
12

Je dis dans le but de programmer des compétitions. Généralement, ils ont de très grandes entrées où la division par 2 a lieu plusieurs fois et il est connu que l'entrée est positive ou négative.

x >> 1 sera meilleur que x / 2. J'ai vérifié sur ideone.com en exécutant un programme où plus de 10 ^ 10 divisions par 2 opérations ont eu lieu. x / 2 a pris près de 5,5 s tandis que x >> 1 a pris près de 2,6 s pour le même programme.

Shashwat Kumar
la source
1
Pour les valeurs non signées, un compilateur doit optimiser x/2pour x>>1. Pour les valeurs signées, presque toutes les implémentations définissent x>>1une signification équivalente x/2mais qui peut être calculée plus rapidement lorsqu'elle xest positive, et qui est utilement différente de x/2lorsqu'elle xest négative.
supercat
12

Je dirais qu'il y a plusieurs choses à considérer.

  1. Bitshift devrait être plus rapide, car aucun calcul spécial n'est vraiment nécessaire pour décaler les bits, mais comme indiqué, il existe des problèmes potentiels avec les nombres négatifs. Si vous êtes assuré d'avoir des nombres positifs et que vous recherchez la vitesse, je recommanderais le bitshift.

  2. L'opérateur de division est très facile à lire pour les humains. Donc, si vous recherchez la lisibilité du code, vous pouvez l'utiliser. Notez que le domaine de l'optimisation du compilateur a parcouru un long chemin, donc rendre le code facile à lire et à comprendre est une bonne pratique.

  3. Selon le matériel sous-jacent, les opérations peuvent avoir des vitesses différentes. La loi d'Amdal est de rendre le cas commun rapide. Vous pouvez donc disposer d'un matériel capable d'effectuer différentes opérations plus rapidement que d'autres. Par exemple, multiplier par 0,5 peut être plus rapide que diviser par 2. (Certes, vous devrez peut-être prendre la parole pour la multiplication si vous souhaitez appliquer la division entière).

Si vous recherchez des performances pures, je recommanderais de créer des tests qui pourraient effectuer les opérations des millions de fois. Échantillonnez l'exécution plusieurs fois (la taille de votre échantillon) pour déterminer laquelle est statistiquement la meilleure avec votre système d'exploitation / matériel / compilateur / code.

James Oravec
la source
2
"Bitshift devrait être plus rapide". compilateurs optimiseront les divisions en décalages de bits
Trevor Hickey
J'espère qu'ils le feraient, mais à moins que vous n'ayez accès à la source du compilateur, vous ne pouvez pas en être sûr :)
James Oravec
1
Je recommanderais également le bithift si l'implémentation le gère de la manière la plus courante et si la façon dont on veut gérer les nombres négatifs correspond à ce qui >>correspond et ne correspond pas à ce qui le /fait.
supercat
12

En ce qui concerne le CPU, les opérations de décalage de bits sont plus rapides que les opérations de division. Cependant, le compilateur le sait et optimisera de manière appropriée dans la mesure du possible, afin que vous puissiez coder de la manière la plus logique et la plus simple possible en sachant que votre code fonctionne efficacement. Mais rappelez-vous qu'un unsigned intpeut (dans certains cas) être mieux optimisé qu'un intpour des raisons précédemment indiquées. Si vous n'avez pas besoin d'arithmatique signée, n'incluez pas le bit de signe.

tylerl
la source
11

x = x / 2; est le code approprié à utiliser .. mais une opération dépend de votre propre programme de la façon dont la sortie que vous souhaitez produire.

Gooner
la source
11

Rendez vos intentions plus claires ... par exemple, si vous voulez diviser, utilisez x / 2 et laissez le compilateur l'optimiser pour déplacer l'opérateur (ou autre chose).

Les processeurs d'aujourd'hui ne laisseront pas ces optimisations avoir un impact sur les performances de vos programmes.

Atul Kumar
la source
10

La réponse à cela dépendra de l'environnement dans lequel vous travaillez.

  • Si vous travaillez sur un microcontrôleur 8 bits ou quoi que ce soit sans prise en charge matérielle pour la multiplication, le décalage de bits est prévu et courant, et bien que le compilateur se transformera presque certainement x /= 2en x >>= 1, la présence d'un symbole de division soulèvera plus de sourcils dans cet environnement que en utilisant un décalage pour effectuer une division.
  • Si vous travaillez dans un environnement ou une section de code critique pour les performances, ou si votre code peut être compilé avec l'optimisation du compilateur désactivée, x >>= 1un commentaire expliquant son raisonnement est probablement préférable pour des raisons de clarté.
  • Si vous n'êtes pas dans l'une des conditions ci-dessus, rendez votre code plus lisible en utilisant simplement x /= 2. Mieux vaut sauver le prochain programmeur qui regarde votre code la double prise de 10 secondes sur votre opération de décalage que de prouver inutilement que vous saviez que le décalage était plus efficace sans optimisation du compilateur.

Tout cela suppose des entiers non signés. Le changement simple n'est probablement pas ce que vous souhaitez signer. En outre, DanielH soulève un bon point sur l'utilisation x *= 0.5de certains langages comme ActionScript.

gkimsey
la source
8

mod 2, testez pour = 1. ne sais pas la syntaxe en c. mais cela peut être le plus rapide.

circusdei
la source
7

généralement le décalage à droite divise:

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

ceci est parfois utilisé pour accélérer les programmes au détriment de la clarté. Je ne pense pas que tu devrais le faire. Le compilateur est suffisamment intelligent pour effectuer automatiquement l'accélération. Cela signifie que vous ne gagnez rien au détriment de la clarté .

Jetez un œil à cette page de la programmation C ++ pratique.

Mouna Cheikhna
la source
Si l'on veut calculer la valeur qui, par exemple (x+128)>>8, calculerait pour des valeurs qui xne sont pas proches du maximum, comment pourrait-on le faire de manière concise sans décalage? Notez que (x+128)/256cela ne fonctionnera pas. Connaissez-vous une belle expression qui le fera?
supercat
7

Évidemment, si vous écrivez votre code pour le prochain gars qui le lira, optez pour la clarté de "x / 2".

Cependant, si la vitesse est votre objectif, essayez-le dans les deux sens et chronométrez les résultats. Il y a quelques mois, j'ai travaillé sur une routine de convolution bitmap qui impliquait de parcourir un tableau d'entiers et de diviser chaque élément par 2. J'ai fait toutes sortes de choses pour l'optimiser, y compris l'ancienne astuce consistant à remplacer "x >> 1" par "x / 2 ".

Quand j'ai effectivement chronométré les deux sens, j'ai découvert à ma grande surprise que x / 2 était plus rapide que x >> 1

Cela utilisait Microsoft VS2008 C ++ avec les optimisations par défaut activées.

Chris Bennet
la source
4

En termes de performances. Les opérations de décalage du processeur sont nettement plus rapides que les codes d'opération de division. Donc, en divisant par deux ou en multipliant par 2, etc., tous bénéficient d'opérations de quart de travail.

Quant à l'apparence. En tant qu'ingénieurs, quand sommes-nous devenus si attachés aux cosmétiques que même les belles femmes ne les utilisent pas! :)

Farjad
la source
3

X / Y est correct ... et l'opérateur de décalage ">>" ... si nous voulons que deux divisent un entier, nous pouvons utiliser l'opérateur de dividende (/). L'opérateur shift est utilisé pour décaler les bits.

x = x / 2; x / = 2; nous pouvons utiliser comme ça ..

chocolat garçon
la source
0

Alors que x >> 1 est plus rapide que x / 2, l'utilisation appropriée de >> lorsqu'il s'agit de valeurs négatives est un peu plus compliquée. Cela nécessite quelque chose de semblable au suivant:

// Extension Method
public static class Global {
    public static int ShiftDivBy2(this int x) {
        return (x < 0 ? x + 1 : x) >> 1;
    }
}
Darin
la source