web-dev-qa-db-fra.com

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

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;

Ici, x est un entier.

399
Abhineet

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 le 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)

845
Mark Byers

Le premier ressemble-t-il à une division? Non. Si vous voulez diviser, utilisez x / 2. Le compilateur peut optimiser l'utilisation du 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.

224
Cat Plus Plus

Pour s'entasser: il y a tellement de raisons de préférer utiliser x = x / 2; Voici quelques-unes:

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

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

  • 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 la performance de votre programme de manière mesurable est elle-même extrêmement faible (et si cela l'affecte de manière mesurable, vous avez un raison d'utiliser un poste)

  • si la division doit faire partie d'une expression plus grande, il est plus probable que vous obteniez la priorité correctement 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

  • réitérer - le compilateur le fera déjà pour vous de toute façon. En fait, il convertit la division par une constante en une série de décalages, ajoute et 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 quart lorsque vous voulez vraiment multiplier ou diviser, sauf peut-être une possibilité accrue d'introduire un bogue. Cela fait toute une vie que les compilateurs ne sont pas assez intelligents pour optimiser ce genre de choses en un virage, le cas échéant.

189
Michael Burr

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

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

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

Si vous voulez diviser un nombre par 2, allez avec le second.

Les deux ne sont pas équivalents, ils ne se comportent pas de la même manière si le nombre est négatif ou dans des expressions plus grandes - bitshift a une priorité inférieure à + ou -, la division ayant une priorité plus élevée.

Vous devriez écrire votre code pour exprimer son intention. Si la performance est votre préoccupation, ne vous inquiétez pas, l'optimiseur fait un bon travail à ce type de micro-optimisations.

62
Luchian Grigore

Utilisez simplement divide (/), en supposant que ce soit plus clair. Le compilateur optimisera en conséquence.

58
justin

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

Cependant, une autre raison de préférer x / 2 à x >> 1 est que le comportement de >> dépend de la mise en œuvre si x est un signe int et est négatif.

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

Le résultat de E1 >> E2 est E1 position décalée _ à droite E2. Si E1 a un type non signé ou si E1 a un type signé et une valeur non négative, la valeur du résultat fait partie intégrante du quotient de E1/2E2. Si E1 a un type signé et une valeur négative, la valeur résultante est définie par l'implémentation.

38
jamesdlin

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

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

(Ceci est une question C/C++, les autres langages de programmation ont plus d'opérateurs. Pour Java, il existe également le décalage à droite non signé, x >>> 1, qui est à nouveau différent. Il permet de calculer correctement la moyenne (moyenne) valeur de deux valeurs, de sorte que (a + b) >>> 1 renvoie la valeur moyenne même pour les très grandes valeurs de a et b. Ceci est requis par exemple pour la recherche binaire si les indices du tableau peut devenir très volumineux. Il y avait n bogue dans de nombreuses versions de la recherche binaire , car ils utilisaient (a + b) / 2 pour calculer la moyenne. Cela ne fonctionne pas correctement. La solution correcte consiste à utiliser (a + b) >>> 1 à la place.)

32
Thomas Mueller

Knuth dit:

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 aussi que l'optimisation de cette opération sous cette forme ne signifie pas une grande différence pour le processeur.

22
Ivan Californias

Regardez la sortie du compilateur pour vous aider à décider. J'ai couru ce test sur x86-64 avec
gcc (GCC) 4.2.1 20070719 [FreeBSD]

Voir aussi sorties du compilateur en ligne à godbolt .

Ce que vous voyez, c'est que le compilateur utilise une instruction sarl (décalage arithmétique à droite) dans les deux cas, de sorte qu'il reconnaît la similarité entre les deux expressions. Si vous utilisez la division, le compilateur doit également s'ajuster aux nombres négatifs. Pour ce faire, le bit de signe est décalé vers le bit le plus faible et ajouté au résultat. Cela corrige le problème "hors-par-un" lors du changement de nombres négatifs, par rapport à ce que ferait une division.
Étant donné que le cas de division fait deux décalages, alors que le cas de décalage explicite n'en fait qu'un, nous pouvons maintenant expliquer certaines des différences de performance mesurées par d'autres réponses ici.

Code C avec sortie Assembly:

Pour diviser, votre contribution serait

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

et cela compile à

    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
19
Michael Donohue

Juste une note ajoutée -

x * = 0.5 sera souvent plus rapide dans certains langages basés sur une machine virtuelle, notamment ActionScript, car il ne sera pas nécessaire de vérifier la variable pour la division par 0.

15
ansiart

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

13
Imdad

Je dis dans le but de programmer des compétitions. Généralement, ils ont des entrées très importantes 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 comportant plus de 10 ^ 10 opérations divisées par 2. x/2 prenait près de 5,5 secondes alors que x >> 1 prenait près de 2,6 secondes pour le même programme.

12
Shashwat Kumar

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

  1. Le décalage de bits devrait être plus rapide, car aucun calcul particulier n'est vraiment nécessaire pour déplacer 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 de la vitesse, je vous recommanderais bitshift.

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

  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. Il est donc possible que votre matériel puisse effectuer différentes opérations plus rapidement que d’autres. Par exemple, multiplier par 0,5 peut être plus rapide que diviser par 2. (Accordé, vous devrez peut-être prendre la parole pour multiplier si vous souhaitez appliquer la division de nombre entier).

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

12
James Oravec

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

12
tylerl

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

11
Gooner

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

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

11
Atul Kumar

La réponse à cette question dépend de l'environnement dans lequel vous travaillez.

  • Si vous travaillez sur un microcontrôleur 8 bits ou sur tout matériel sans support matériel pour la multiplication, le décalage de bits est courant et banal, et même si le compilateur va presque certainement transformer x /= 2 en x >>= 1, la présence d'un Le symbole de division lèvera plus de sourcils dans cet environnement que d'utiliser un décalage pour effectuer une division.
  • Si vous travaillez dans un environnement ou une section de code critique en termes de performances, ou si votre code peut être compilé avec l'optimisation du compilateur désactivée, x >>= 1 avec un commentaire expliquant son raisonnement est probablement préférable pour des raisons de clarté.
  • Si vous ne remplissez pas l'une des conditions ci-dessus, améliorez la lisibilité de votre code en utilisant simplement x /= 2. Mieux vaut sauvegarder le prochain programmeur qui examine votre code la double prise de 10 secondes sur votre opération de quart plutôt que de prouver inutilement que vous saviez que le décalage était plus efficace sans l'optimisation du compilateur.

Tous ceux-ci supposent des entiers non signés. Le simple changement n'est probablement pas ce que vous voulez pour signé. En outre, DanielH soulève un point intéressant concernant l'utilisation de x *= 0.5 pour certains langages tels qu'ActionScript.

10
gkimsey

mod 2, test pour = 1. dunno la syntaxe dans c. mais c'est peut-être le plus rapide.

8
circusdei

généralement, le bon décalage divise:

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

cela 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 accélérer automatiquement. Cela signifie que le changement de poste ne vous rapporte rien au détriment de la clarté .

Jetez un coup d’œil à cette page de Practical C++ Programming.

7
Mouna Cheikhna

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

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

Quand j’ai chronométré dans 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.

7
Chris Bennet

En termes de performance. Les opérations de décalage du processeur sont nettement plus rapides que les codes d'opération divisés. Donc, en divisant par deux ou en multipliant par 2, etc., tous tirent parti des opérations par équipes.

En ce qui concerne l'apparence. En tant qu'ingénieurs, quand sommes-nous devenus si attachés aux cosmétiques que même les belles dames n'utilisent pas! :)

4
Farjad

X/Y est un opérateur correct ... "et" >>. Si nous voulons diviser un entier en deux, nous pouvons utiliser (/) l’opérateur de dividende. opérateur de décalage est utilisé pour déplacer les bits ..

x = x/2; x/= 2; on peut utiliser comme ça ..

3
chocolate boy

Bien que x >> 1 soit plus rapide que x/2, l'utilisation correcte de >> pour traiter les valeurs négatives est un peu plus compliquée. Cela nécessite quelque chose de similaire à ce qui suit:

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