Comment vérifier si un entier est pair ou impair? [fermé]


193

Comment puis-je vérifier si un nombre donné est pair ou impair en C?


5
La version qui utilise au niveau du bit et (&) est beaucoup plus efficace que la version modulo (%). Vous devez changer celle que vous avez sélectionnée comme réponse correcte.
Stefan Rusek

6
Peu probable - l'argument est une constante. Facile pour l'optimiseur
MSalters

2
La lisibilité prend également en compte ces facteurs.
Brian G

2
Dans les applications embarquées (le monde où je passe la plupart de mon temps de programmation), certains processeurs ont des unités arithmétiques très primitives et ne peuvent pas faire facilement des opérations de division / module. Pour cette raison, j'utilise généralement la méthode bit à bit et. Cependant, sur le processeur d'un ordinateur de bureau moderne, ce ne sera pas le cas.
bta

3
Je n'ai jamais trouvé l'opération de module plus facile à comprendre. Lorsque j'ai d'abord eu besoin de déterminer pair ou impair, le masque au niveau du bit a été la première chose qui m'est venue à l'esprit. C'est un peu naturel, car la façon dont nous avons tendance à le faire à la main est de regarder le chiffre le moins significatif pour voir s'il est dans {0 2 4 6 8} ou {1 3 5 7 9}. Cela se traduit directement par la recherche du bit le moins significatif pour voir si c'est 0 ou 1.
P Daddy

Réponses:


449

Utilisez l'opérateur modulo (%) pour vérifier s'il y a un reste lors de la division par 2:

if (x % 2) { /* x is odd */ }

Quelques personnes ont critiqué ma réponse ci-dessus en déclarant que l'utilisation de x & 1 est "plus rapide" ou "plus efficace". Je ne pense pas que ce soit le cas.

Par curiosité, j'ai créé deux programmes de cas de test triviaux:

/* modulo.c */
#include <stdio.h>

int main(void)
{
    int x;
    for (x = 0; x < 10; x++)
        if (x % 2)
            printf("%d is odd\n", x);
    return 0;
}

/* and.c */
#include <stdio.h>

int main(void)
{
    int x;
    for (x = 0; x < 10; x++)
        if (x & 1)
            printf("%d is odd\n", x);
    return 0;
}

Je les ai ensuite compilés avec gcc 4.1.3 sur l'une de mes machines 5 fois différentes:

  • Sans indicateurs d'optimisation.
  • Avec -O
  • Avec -Os
  • Avec -O2
  • Avec -O3

J'ai examiné la sortie d'assemblage de chaque compilation (en utilisant gcc -S) et j'ai constaté que dans chaque cas, la sortie pour and.c et modulo.c étaient identiques (ils utilisaient tous les deux l'instruction andl $ 1,% eax). Je doute que ce soit une "nouvelle" fonctionnalité, et je soupçonne qu'elle remonte à des versions anciennes. Je doute également que tout compilateur moderne (fabriqué au cours des 20 dernières années) non arcane, commercial ou open source, manque d'une telle optimisation. Je testerais sur d'autres compilateurs, mais je n'en ai pas pour le moment.

Si quelqu'un d'autre voulait tester d'autres compilateurs et / ou cibles de plate-forme et obtenir un résultat différent, je serais très intéressé de le savoir.

Enfin, la version modulo est garantie par la norme de fonctionner que l'entier soit positif, négatif ou nul, quelle que soit la représentation de l'implémentation des entiers signés. La version bit à bit et n'est pas. Oui, je me rends compte que le complément à deux est quelque peu omniprésent, donc ce n'est pas vraiment un problème.


11
La question demandait spécifiquement comment le faire en C, donc j'ai répondu en C, malgré le fait que Chustar ait mentionné qu'ils ne pouvaient pas trouver comment le faire en Java. Je n'ai pas prétendu ou laissé entendre que c'était une réponse Java, je ne connais pas Java. Je pense que je viens de recevoir mon premier downvote et je ne comprends pas pourquoi. Tant pis.
Chris Young

33
Je dirais que si (x% 2! = 0) {/ * x est impair * /}, mais qui sait. Je ne connais pas java non plus.
eugensk

9
Il y a beaucoup de votes positifs pour le distinguer des imbéciles d'opérateurs au niveau du bit, sans avoir à dépenser notre karma pour les voter.
wnoise

13
Je suis d'accord avec tout, sauf une chose: j'aime garder les entiers et les valeurs de vérité séparées, conceptuellement, donc je préfère écrire "si (x% 2 == 1)". C'est la même chose pour le compilateur, mais peut-être un peu plus clair pour les humains. De plus, vous pouvez utiliser le même code dans des langues qui n'interprètent pas non nul comme vrai.
Thomas Padron-McCarthy

46
Ma référence? Quelle référence? Je n'ai fait aucune analyse comparative. J'ai examiné le langage d'assemblage généré. Cela n'a absolument rien à voir avec printf.
Chris Young

207

Vous êtes waaaaaaaay trop efficaces. Ce que vous voulez vraiment, c'est:

public boolean isOdd(int num) {
  int i = 0;
  boolean odd = false;

  while (i != num) {
    odd = !odd;
    i = i + 1;
  }

  return odd;
}

Répétez l'opération pour isEven.

Bien sûr, cela ne fonctionne pas pour les nombres négatifs. Mais avec éclat vient le sacrifice ...


17
Si vous avez jeté une exception d'argument sur des valeurs négatives et noté dans la documentation que cette fonction est O (N), alors je serais très bien avec cela.
Jeffrey L Whitledge

7
La version entreprise devrait utiliser XML. Bien sûr, de nos jours, vous auriez un service Web que vous pourriez interroger
Martin Beckett

58
Vous devez optimiser cela avec une table de correspondance.
Weeble

1
Je suis un tel moine, j'ai dû attribuer +1 à vos 6 999 représentants dans un nouveau millénaire
Eran Medan

7
C'est génial! Mon patron m'a dit que nous avions un client qui était en colère parce qu'il pensait que sa licence d'entreprise ne donnait rien de plus que la licence standard. Maintenant que nous avons ajouté cette fonction dans notre programme, et juste parce qu'elle s'exécute plus lentement, il pense que son logiciel fait BEAUCOUP plus de travail !!!
Phil

97

Utilisez l'arithmétique des bits:

if((x & 1) == 0)
    printf("EVEN!\n");
else
    printf("ODD!\n");

C'est plus rapide que d'utiliser la division ou le module.


43
Je ne pense pas qu'il soit juste de dire que c'est plus rapide que d'utiliser la division ou le module. La norme C ne dit rien sur les performances des opérateurs, et tout compilateur décent produira du code rapide pour les deux. Je choisirais personnellement l'idiome qui communique mon intention, et% semble plus approprié ici
Chris Young

21
J'aime mieux (x & 1), car il vérifie si le nombre est le même que les autres: vérifiez si le dernier chiffre est pair ou impair. À mon avis, elle communique plus son intention que la méthode modulo. (Pas que ça compte beaucoup.)
Jeremy Ruten

2
Tu as raison, je suppose que c'est subjectif. Bien que la définition habituelle de "pair" soit "entier divisible par 2", pas "entier se terminant par 0, 2, 4, 6 ou 8". :-)
Chris Young

4
@TraumaPony - pour la norme C ANSI et Java précoce, dépend du système informatique. On ne sait pas quelle représentation est utilisée pour les nombres signés - compliment de 2, compliment de 1, code gris, etc. Mais le module est toujours le module
Aaron

9
Ne fonctionne pas universellement pour les nombres négatifs. Voir Vérifier cette réponse pour plus de détails: stackoverflow.com/questions/160930/… pour plus de détails.
Andrew Edgecombe

36

[Mode blague = "on"]

public enum Evenness
{
  Unknown = 0,
  Even = 1,
  Odd = 2
}

public static Evenness AnalyzeEvenness(object o)
{

  if (o == null)
    return Evenness.Unknown;

  string foo = o.ToString();

  if (String.IsNullOrEmpty(foo))
    return Evenness.Unknown;

  char bar = foo[foo.Length - 1];

  switch (bar)
  {
     case '0':
     case '2':
     case '4':
     case '6':
     case '8':
       return Evenness.Even;
     case '1':
     case '3':
     case '5':
     case '7':
     case '9':
       return Evenness.Odd;
     default:
       return Evenness.Unknown;
  }
}

[Mode blague = "désactivé"]

EDIT: Ajout de valeurs déroutantes à l'énumération.


2
Wow ... c'est plus dément que la solution de SCdF! Gloire! Pas de vote positif cependant ... je ne peux pas recommander cela. Mais merci pour le drôle!
Wes P

1
L'avantage de cette approche est qu'elle fonctionne avec plus que de simples chiffres. De plus, si vous remplacez cette ligne: char bar = foo [foo.Length - 1]; avec ceci: double barre = Char.GetNumericValue (foo [foo.Length - 1]); Ensuite, cela fonctionnera avec n'importe quel système numérique.
Jeffrey L Whitledge

5
rapport de bogue: 14,65 est signalé comme impair alors qu'il devrait être inconnu.
TheSoftwareJedi

4
Logiciel Jedi, c'est une "fonctionnalité". ;)
Sklivvz

31
TheSoftwareJedi: 14,65 est l'un des entiers les plus étranges que j'ai jamais vus.
Bruce Alderman

16

En réponse à ffpf - j'ai eu exactement le même argument avec un collègue il y a des années, et la réponse est non , cela ne fonctionne pas avec des nombres négatifs.

La norme C stipule que les nombres négatifs peuvent être représentés de 3 manières:

  • Complément de 2
  • Complément de 1
  • signe et ampleur

Vérification comme ceci:

isEven = (x & 1);

fonctionnera pour le complément à 2 et le signe et la représentation de l'amplitude, mais pas pour le complément à 1.

Cependant, je pense que ce qui suit fonctionnera dans tous les cas:

isEven = (x & 1) ^ ((-1 & 1) | ((x < 0) ? 0 : 1)));

Merci à ffpf d'avoir souligné que la zone de texte mangeait tout après mon caractère inférieur à!


Je pense que votre deuxième exemple de code manque de texte.
Jeff Yates

3
Complimentons ces chiffres!
2013

14

Un bon est:

/*forward declaration, C compiles in one pass*/
bool isOdd(unsigned int n);

bool isEven(unsigned int n)
{
  if (n == 0) 
    return true ;  // I know 0 is even
  else
    return isOdd(n-1) ; // n is even if n-1 is odd
}

bool isOdd(unsigned int n)
{
  if (n == 0)
    return false ;
  else
    return isEven(n-1) ; // n is odd if n-1 is even
}

Notez que cette méthode utilise la récursivité de queue impliquant deux fonctions. Il peut être implémenté efficacement (transformé en une sorte de boucle while / until) si votre compilateur prend en charge la récursivité de queue comme un compilateur Scheme. Dans ce cas, la pile ne doit pas déborder!


1
Cela ne gère pas bien isOdd (0).
Steve McLeod

1
Je pense que vous avez une boucle infinie (avec récursion de queue) ou un débordement de pile (sans récursion de queue) pour isOdd () avec des valeurs paires ou isEven () avec des valeurs impaires. Il se termine uniquement par true. C'est à nouveau le problème de l'arrêt.
Jeffrey L Whitledge

7
Oh, bien sûr, corrigez-le sans commentaire et faites-moi ressembler à un idiot. C'est très bien.
Jeffrey L Whitledge

1
Maintenant, vous avez une erreur de compilation: dans isEven, tous les chemins de code ne renvoient pas de valeur. Non, je n'ai pas vraiment essayé ce code, c'est le compilateur dans ma tête qui se plaint.
Jeffrey L Whitledge

5
erreur de compilation: tous les chemins ne renvoient pas une valeur de haine pour vous bombarder de commentaires de bogues sur votre exemple de code, mais que se passe-t-il lorsque vous appelez isEven (5)
Kevin

11

Un nombre est pair si, lorsqu'il est divisé par deux, le reste est 0. Un nombre est impair si, lorsqu'il est divisé par 2, le reste est 1.

// Java
public static boolean isOdd(int num){
    return num % 2 != 0;
}

/* C */
int isOdd(int num){
    return num % 2;
}

Les méthodes sont super!


Votre méthode Java est rompue car num% 2 == -1 pour les nombres impairs négatifs.
WMR

C'est pour ça que tu m'as déçu?
jjnguy

3
Je l'ai rétrogradé parce que votre fonction en C prend plus de caractères à taper que ce qu'elle fait. IE num% I est de 7 caractères, y compris les espaces IsOdd (I) est de 8 caractères. Pourquoi voudriez-vous créer une fonction plus longue que la simple opération?
Kevin

13
@Kevin à mon avis, le code n'est pas mesuré par les caractères, mais plutôt par le temps qu'il vous faut pour l'écrire, y compris le temps de réflexion et de débogage. num% 2 prend une milliseconde de plus à penser qu'isOdd. ajoutez maintenant les chiffres à l'échelle mondiale et vous avez perdu une année collective. isOdd peut également être testé, vérifié et éventuellement certifié sans bogue (par exemple, la gestion des nombres négatifs) où, comme num% 2 - certains développeurs auront toujours un doute et iront expérimenter. un bon code est un code que vous n'écrivez pas, réutilisez simplement ... juste mes 2 cents.
Eran Medan

2
@EranMedan, La même logique s'appliquerait au remplacement de i ++ par IncrementByOne (i) et c'est tout aussi mauvais une idée. Si un développeur a un doute sur ce que fait% 2, je ne veux pas qu'il soit proche de mon code.
Kevin


7

Je dirais juste le diviser par 2 et s'il y a un reste de 0, c'est pair, sinon c'est impair.

L'utilisation du module (%) facilite cela.

par exemple. 4% 2 = 0 donc 4 est pair 5% 2 = 1 donc 5 est impair


6

Une solution de plus au problème
(les enfants sont les bienvenus pour voter)

bool isEven(unsigned int x)
{
  unsigned int half1 = 0, half2 = 0;
  while (x)
  {
     if (x) { half1++; x--; }
     if (x) { half2++; x--; }

  }
  return half1 == half2;
}

Non, tu
n'es

J'allais voter contre, mais c'est un peu lent sur les nombres négatifs. :)
Chris Young

3
Tous les chiffres sont brillants et positifs. Ou avez-vous des préjugés contre certains? :))
eugensk

3
Dans les ordinateurs, tous les nombres, une fois négatifs, finissent par devenir positifs. Nous l'appelons le Rollover of Happiness (non applicable aux BIGNUMS, YMMY, non valable dans tous les états).
Will Hartung

@WillHartung "roulement de bonheur" est super! : D
thejh

6

Je construirais un tableau des parités (0 si même 1 si impair) des entiers (donc on pourrait faire une recherche: D), mais gcc ne me laissera pas faire des tableaux de telles tailles:

typedef unsigned int uint;

char parity_uint [UINT_MAX];
char parity_sint_shifted [((uint) INT_MAX) + ((uint) abs (INT_MIN))];
char* parity_sint = parity_sint_shifted - INT_MIN;

void build_parity_tables () {
    char parity = 0;
    unsigned int ui;
    for (ui = 1; ui <= UINT_MAX; ++ui) {
        parity_uint [ui - 1] = parity;
        parity = !parity;
    }
    parity = 0;
    int si;
    for (si = 1; si <= INT_MAX; ++si) {
        parity_sint [si - 1] = parity;
        parity = !parity;
    }
    parity = 1;
    for (si = -1; si >= INT_MIN; --si) {
        parity_sint [si] = parity;
        parity = !parity;
    }
}

char uparity (unsigned int n) {
    if (n == 0) {
        return 0;
    }
    return parity_uint [n - 1];
}

char sparity (int n) {
    if (n == 0) {
        return 0;
    }
    if (n < 0) {
        ++n;
    }
    return parity_sint [n - 1];
}

Alors, utilisons plutôt la définition mathématique de pair et impair.

Un entier n est même s'il existe un entier k tel que n = 2k.

Un entier n est impair s'il existe un entier k tel que n = 2k + 1.

Voici le code pour cela:

char even (int n) {
    int k;
    for (k = INT_MIN; k <= INT_MAX; ++k) {
        if (n == 2 * k) {
            return 1;
        }
    }
    return 0;
}

char odd (int n) {
    int k;
    for (k = INT_MIN; k <= INT_MAX; ++k) {
        if (n == 2 * k + 1) {
            return 1;
        }
    }
    return 0;
}

Soit C-entiers les valeurs possibles de intdans une compilation C donnée. (Notez que les C-entiers sont un sous-ensemble des entiers.)

Maintenant, on pourrait craindre que pour un n donné dans les entiers C, l'entier k correspondant puisse ne pas exister dans les entiers C. Mais avec une petite preuve, on peut montrer que pour tous les entiers n, | n | <= | 2n | (*), où | n | est "n si n est positif et -n sinon". En d'autres termes, pour tous les n en nombres entiers, au moins une des conditions suivantes (exactement soit les cas (1 et 2) soit les cas (3 et 4) en fait, mais je ne le prouverai pas ici):

Cas 1: n <= 2n.

Cas 2: -n <= -2n.

Cas 3: -n <= 2n.

Cas 4: n <= -2n.

Maintenant, prenez 2k = n. (Un tel ak existe si n est pair, mais je ne le prouverai pas ici. Si n n'est pas pair alors la boucle dans evenne revient pas tôt de toute façon, donc cela n'a pas d'importance.) Mais cela implique k <n si n pas 0 par (*) et le fait (encore une fois non prouvé ici) que pour tout m, z dans les entiers 2m = z implique que z n'est pas égal à m étant donné m n'est pas 0. Dans le cas n est 0, 2 * 0 = 0 donc 0 est pair, nous avons terminé (si n = 0, alors 0 est en C-entiers car n est en C-entier dans la fonction even, donc k = 0 est en C-entiers). Ainsi, un tel k dans les entiers C existe pour n dans les entiers C si n est pair.

Un argument similaire montre que si n est impair, il existe ak dans les entiers C tels que n = 2k + 1.

Par conséquent , les fonctions evenet oddprésentées ici fonctionnent correctement pour tous les C-entiers.


1
Je ne parle pas d'offense, mais quel est le point de cette réponse? i % 2est beaucoup plus petit et probablement plus efficace.
GManNickG

2
@GMan: Mais c'est beaucoup plus déterministe! Cela fonctionnera correctement pour détecter tous les cas de bord.
P Daddy

1
... ET (!!!) c'est correct !!!
Thomas Eding

Je ne peux pas dire si tu plaisantes ou pas. : X %2fonctionne pour tous les entiers.
GManNickG

1
+1: J'allais dire "bonne réponse", mais je pense que "réponse intéressante" est plus appropriée.
James Webster

5
// C#
bool isEven = ((i % 2) == 0);

2
Quoi? Ce n'est pas C #! C'est du pur C! :-P
asterite

8
Je vais lancer un WinForm autour pour le rendre pur C # ...
Michael Petrotta

@mateusza: Habituellement, lorsque vous voyez "bool" dans une majuscule ou une autre en C, c'est un typedefou #defineou quelque chose.
David Thornley

2
@mateusza @David Thornley Dans C99 bool est une fonctionnalité standard ( en.wikipedia.org/wiki/Stdbool.h )
fortran

1
Parlez de parenthèses extrêmement redondantes ...
Thomas Eding

4

Voici une réponse en Java:

public static boolean isEven (Integer Number) {
    Pattern number = Pattern.compile("^.*?(?:[02]|8|(?:6|4))$");
    String num = Number.toString(Number);
    Boolean numbr = new Boolean(number.matcher(num).matches());
    return numbr.booleanValue();
}

4

Essaye ça: return (((a>>1)<<1) == a)

Exemple:

a     =  10101011
-----------------
a>>1 --> 01010101
a<<1 --> 10101010

b     =  10011100
-----------------
b>>1 --> 01001110
b<<1 --> 10011100

Pouvez-vous expliquer cela s'il vous plaît? Je ne connais pas très bien les opérateurs au niveau du bit
Abdul

Déplacer vers la droite puis vers la gauche mettra à zéro votre dernier bit (le plus à droite). Si le nouveau numéro est le même que l'original, cela signifie que le dernier bit du numéro d'origine était 0. Il est donc pair. Jetez un oeil à ma réponse mise à jour.
Kiril Aleksandrov

merci, je comprends maintenant
Abdul

Je ne sais pas quelle approche est la plus rapide. Je n'ai pas essayé de les comparer.
Kiril Aleksandrov

Cela ne met-il pas également à zéro votre partie la plus importante? Un problème avec des entiers non signés dans certaines langues et des entiers négatifs dans la plupart ...
Troyseph

4

En lisant cette discussion plutôt divertissante, je me suis souvenu que j'avais une fonction réelle, sensible au temps, qui testait les nombres pairs et impairs dans la boucle principale. C'est une fonction de puissance entière, publiée ailleurs sur StackOverflow, comme suit. Les repères étaient assez surprenants. Au moins dans cette fonction du monde réel, le modulo est plus lent , et de manière significative. Le gagnant, par une large marge, nécessitant 67% du temps de modulo, est une approche ou (|) , et ne se trouve nulle part ailleurs sur cette page.

static dbl  IntPow(dbl st0, int x)  {
    UINT OrMask = UINT_MAX -1;
    dbl  st1=1.0;
    if(0==x) return (dbl)1.0;

    while(1 != x)   {
        if (UINT_MAX == (x|OrMask)) {     //  if LSB is 1...    
        //if(x & 1) {
        //if(x % 2) {
            st1 *= st0;
        }    
        x = x >> 1;  // shift x right 1 bit...  
        st0 *= st0;
    }
    return st1 * st0;
}

Pour 300 millions de boucles, les délais de référence sont les suivants.

3.962 le | et approche par masque

4.851 l'approche &

5.850 l'approche%

Pour les personnes qui pensent que la théorie ou une liste de langage d'assemblage règle des arguments comme ceux-ci, cela devrait être un récit édifiant. Il y a plus de choses dans le ciel et la terre, Horatio, que ce dont vous rêvez dans votre philosophie.


1
Mieux vaut l'utiliser unsigned xtel que le x = x >> 1;comportement défini par l'implémentation lorsque x < 0. Pas clair pourquoi xet OrMaskdiffèrent en type. Assez simple pour réécrire à l'aide d'un while(x)test.
chux

2
Je me demande quel compilateur vous avez utilisé pour comparer cela, car la plupart des compilateurs devraient être assez intelligents pour compiler le % 2cas en utilisant le bit &. Je viens de tester cela et les résultats sont complètement les mêmes (VS2015, versions de version avec toutes les optimisations, x86 et x64). La réponse acceptée l'indique également pour GCC (écrite en 2008).
Lou

2
Le problème avec ce message est que la prémisse qu'un bitwise orserait plus rapide qu'un an andest très peu probable, sur n'importe quelle plate-forme / compilateur. Même s'il y avait un combo plateforme / compilateur aussi étrange (et que vous n'avez pas posté ni cela ni le code utilisé pour effectuer le benchmark), dépendre d'autres compilateurs pour se comporter de la même manière serait un mauvais pari d'optimisation. Donc, comme je l'ai écrit, je me demande sur quelle plateforme / compilateur cela a été testé , car je suis presque certain qu'il n'a pas été mesuré correctement.
Lou

2
Ne pas vous appeler un menteur, prétendre simplement avec une grande certitude que vous n'avez pas mesuré correctement. Pas besoin de m'appeler chauffeur de camion pour le moment, lisez mon commentaire d'origine: j'ai fait un point de repère, et les résultats étaient, comme prévu, complètement les mêmes dans les trois cas (certitude de ~ 3 sigma, après avoir exécuté chaque test 10 fois pour 500.000 .000 itérations). Si vous avez vraiment une longue carrière illustre, prenez du recul et pensez si vos revendications ont du sens, puis affichez le code réel utilisé pour faire le benchmark. Sinon, le message est ce que je crois qu'il est, juste une erreur de mesure.
Lou


4

Il s'agit d'un suivi de la discussion avec @RocketRoy concernant sa réponse , mais il pourrait être utile à quiconque souhaite comparer ces résultats.

tl; dr D'après ce que j'ai vu, l'approche de Roy ( (0xFFFFFFFF == (x | 0xFFFFFFFE)) n'est pas complètement optimisée pour x & 1être l' modapproche, mais dans la pratique, les temps d'exécution devraient se révéler égaux dans tous les cas.

Donc, j'ai d'abord comparé la sortie compilée à l'aide de l' explorateur de compilateur :

Fonctions testées:

int isOdd_mod(unsigned x) {
    return (x % 2);
}

int isOdd_and(unsigned x) {
    return (x & 1);
}

int isOdd_or(unsigned x) {
    return (0xFFFFFFFF == (x | 0xFFFFFFFE));
}   

CLang 3.9.0 avec -O3:

isOdd_mod(unsigned int):                          # @isOdd_mod(unsigned int)
        and     edi, 1
        mov     eax, edi
        ret

isOdd_and(unsigned int):                          # @isOdd_and(unsigned int)
        and     edi, 1
        mov     eax, edi
        ret

isOdd_or(unsigned int):                           # @isOdd_or(unsigned int)
        and     edi, 1
        mov     eax, edi
        ret

GCC 6.2 avec -O3:

isOdd_mod(unsigned int):
        mov     eax, edi
        and     eax, 1
        ret

isOdd_and(unsigned int):
        mov     eax, edi
        and     eax, 1
        ret

isOdd_or(unsigned int):
        or      edi, -2
        xor     eax, eax
        cmp     edi, -1
        sete    al
        ret

Chapeau à CLang, il s'est rendu compte que les trois cas sont fonctionnellement égaux. Cependant, l'approche de Roy n'est pas optimisée dans GCC, donc YMMV.

C'est similaire avec Visual Studio; en inspectant le démontage Release x64 (VS2015) pour ces trois fonctions, j'ai pu voir que la partie de comparaison est égale pour les cas "mod" et "et", et légèrement plus grande pour le cas Roy ou ":

// x % 2
test bl,1  
je (some address) 

// x & 1
test bl,1  
je (some address) 

// Roy's bitwise or
mov eax,ebx  
or eax,0FFFFFFFEh  
cmp eax,0FFFFFFFFh  
jne (some address)

Cependant, après avoir exécuté un benchmark réel pour comparer ces trois options (mod simple, bit à bit ou, bit à bit et), les résultats étaient complètement égaux (encore une fois, Visual Studio 2005 x86 / x64, version Release, aucun débogueur attaché).

L'assemblage de version utilise l' testinstruction pour andet les modcas, tandis que le cas de Roy utilise l' cmp eax,0FFFFFFFFhapproche, mais il est fortement déroulé et optimisé, donc il n'y a pas de différence dans la pratique.

Mes résultats après 20 exécutions (i7 3610QM, plan d'alimentation Windows 10 défini sur Haute performance):

[Test: Plain mod 2] TEMPS MOYEN: 689,29 ms (Diff relative: + 0,000%)
[Test: au niveau du bit ou] TEMPS MOYEN: 689,63 ms (diff. Relative: + 0,048%)
[Test: au niveau du bit et] TEMPS MOYEN: 687,80 ms (diff. Relative: -0,217%)

La différence entre ces options est inférieure à 0,3%, il est donc assez évident que l'assemblage est égal dans tous les cas.

Voici le code si quelqu'un veut essayer, avec une mise en garde que je ne l'ai testé que sur Windows (vérifiez le #if LINUXconditionnel pour la get_timedéfinition et implémentez-le si nécessaire, tiré de cette réponse ).

#include <stdio.h>

#if LINUX
#include <sys/time.h>
#include <sys/resource.h>
double get_time()
{
    struct timeval t;
    struct timezone tzp;
    gettimeofday(&t, &tzp);
    return t.tv_sec + t.tv_usec*1e-6;
}
#else
#include <windows.h>
double get_time()
{
    LARGE_INTEGER t, f;
    QueryPerformanceCounter(&t);
    QueryPerformanceFrequency(&f);
    return (double)t.QuadPart / (double)f.QuadPart * 1000.0;
}
#endif

#define NUM_ITERATIONS (1000 * 1000 * 1000)

// using a macro to avoid function call overhead
#define Benchmark(accumulator, name, operation) { \
    double startTime = get_time(); \
    double dummySum = 0.0, elapsed; \
    int x; \
    for (x = 0; x < NUM_ITERATIONS; x++) { \
        if (operation) dummySum += x; \
    } \
    elapsed = get_time() - startTime; \
    accumulator += elapsed; \
    if (dummySum > 2000) \
        printf("[Test: %-12s] %0.2f ms\r\n", name, elapsed); \
}

void DumpAverage(char *test, double totalTime, double reference)
{
    printf("[Test: %-12s] AVERAGE TIME: %0.2f ms (Relative diff.: %+6.3f%%)\r\n",
        test, totalTime, (totalTime - reference) / reference * 100.0);
}

int main(void)
{
    int repeats = 20;
    double runningTimes[3] = { 0 };
    int k;

    for (k = 0; k < repeats; k++) {
        printf("Run %d of %d...\r\n", k + 1, repeats);
        Benchmark(runningTimes[0], "Plain mod 2", (x % 2));
        Benchmark(runningTimes[1], "Bitwise or", (0xFFFFFFFF == (x | 0xFFFFFFFE)));
        Benchmark(runningTimes[2], "Bitwise and", (x & 1));
    }

    {
        double reference = runningTimes[0] / repeats;
        printf("\r\n");
        DumpAverage("Plain mod 2", runningTimes[0] / repeats, reference);
        DumpAverage("Bitwise or", runningTimes[1] / repeats, reference);
        DumpAverage("Bitwise and", runningTimes[2] / repeats, reference);
    }

    getchar();

    return 0;
}

Je crois que vous avez commis le péché cardinal de l'analyse comparative; en créer un si spécifique qu'il ne représente pas un environnement réel. Regardez votre langage d'assemblage et notez le peu de registres que vous utilisez. Des notes élevées pour l'effort, mais ces résultats ne tiendront pas dans le traitement du monde réel.

@RocketRoy: puisque toutes les sorties sont exactement les mêmes pour les trois cas (enfin, un peu moins bien pour votre programme dans un cas), peu m'importe le nombre de registres utilisés. Mais encore une fois, n'hésitez pas à créer et à publier un exemple de programme / environnement qui confondra le compilateur pour créer un assemblage plus optimisé dans l'un des cas, toutes choses étant égales par ailleurs.
Lou

J'aime toujours les programmeurs arrogants. C'est un bon trait pour un programmeur, mais dans un programme plus complexe et réel, ma méthode fonctionnera mieux que la vôtre car le compilateur a plus de façons de résoudre le problème afin que les instructions se chevauchent (sur les architectures Intel) produisant de meilleurs résultats . Très peu de programmeurs chevronnés ayant une bonne expérience en matière de benchmarking préféreraient votre benchmark, mais continuez votre bon travail et n'oubliez pas de réexécuter vos benchmarks lorsque de nouvelles versions de puces sortiront. Les choses changent avec le temps.

3

Je sais que c'est juste du sucre syntaxique et uniquement applicable en .net mais qu'en est-il de la méthode d'extension ...

public static class RudiGroblerExtensions
{
    public static bool IsOdd(this int i)
    {
        return ((i % 2) != 0);
    }
}

Vous pouvez maintenant effectuer les opérations suivantes

int i = 5;
if (i.IsOdd())
{
    // Do something...
}

1
Beau code. Dommage qu'il prétende que 2 est impair, et 3 ne l'est pas.
Anthony

oups, désolé ... ma logique est à l'envers ...
rudigrobler

3

Dans la "catégorie créative mais déroutante" je vous propose:

int isOdd(int n) { return n ^ n * n ? isOdd(n * n) : n; }

Une variante de ce thème spécifique à Microsoft C ++:

__declspec(naked) bool __fastcall isOdd(const int x)
{
    __asm
    {
        mov eax,ecx
        mul eax
        mul eax
        mul eax
        mul eax
        mul eax
        mul eax
        ret
    }
}

2

La méthode au niveau du bit dépend de la représentation interne de l'entier. Modulo fonctionnera partout où il y a un opérateur modulo. Par exemple, certains systèmes utilisent en fait les bits de bas niveau pour le balisage (comme les langages dynamiques), donc le x & 1 brut ne fonctionnera pas réellement dans ce cas.


2

IsOdd (int x) {return true; }

Preuve d'exactitude - considérez l'ensemble de tous les entiers positifs et supposez qu'il existe un ensemble non vide d'entiers qui ne sont pas impairs. Parce que les entiers positifs sont bien ordonnés, il y aura un plus petit nombre non impair, qui en soi est assez impair, si clairement que ce nombre ne peut pas être dans l'ensemble. Par conséquent, cet ensemble ne peut pas être non vide. Répétez l'opération pour les nombres entiers négatifs, sauf recherchez le plus grand nombre non impair.


2

Portable:

i % 2 ? odd : even;

Unportable:

i & 1 ? odd : even;

i << (BITS_PER_INT - 1) ? odd : even;

2

Comme certaines personnes l'ont signalé, il existe de nombreuses façons de procéder. Selon ce site , le moyen le plus rapide est l'opérateur de module:

if (x % 2 == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

Cependant, voici un autre code qui a été marqué par l'auteur et qui a fonctionné plus lentement que l'opération de module commune ci-dessus:

if ((x & 1) == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

System.Math.DivRem((long)x, (long)2, out outvalue);
        if ( outvalue == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

if (((x / 2) * 2) == x)
               total += 1; //even number
        else
               total -= 1; //odd number

if (((x >> 1) << 1) == x)
               total += 1; //even number
        else
               total -= 1; //odd number

        while (index > 1)
               index -= 2;
        if (index == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

tempstr = x.ToString();
        index = tempstr.Length - 1;
        //this assumes base 10
        if (tempstr[index] == '0' || tempstr[index] == '2' || tempstr[index] == '4' || tempstr[index] == '6' || tempstr[index] == '8')
               total += 1; //even number
        else
               total -= 1; //odd number

Combien de personnes connaissaient même la méthode Math.System.DivRem ou pourquoi l'utilisaient-elles ??



1

Pour donner plus de détails sur la méthode de l'opérateur au niveau du bit pour ceux d'entre nous qui n'ont pas fait beaucoup d'algèbre booléenne pendant nos études, voici une explication. Probablement pas d'une grande utilité pour l'OP, mais j'avais envie de préciser pourquoi NUMBER & 1 fonctionne.

Veuillez noter que, comme quelqu'un a répondu ci-dessus, la façon dont les nombres négatifs sont représentés peut empêcher cette méthode de fonctionner. En fait, il peut même casser la méthode de l'opérateur modulo, car chaque langue peut différer dans la façon dont elle traite les opérandes négatifs.

Cependant, si vous savez que NUMBER sera toujours positif, cela fonctionne bien.

Comme Tooony ci-dessus a fait remarquer que seul le dernier chiffre en binaire (et déni) est important.

Une logique booléenne ET porte que les deux entrées doivent être un 1 (ou haute tension) pour que 1 soit renvoyé.

1 & 0 = 0.

0 & 1 = 0.

0 et 0 = 0.

1 & 1 = 1.

Si vous représentez n'importe quel nombre comme binaire (j'ai utilisé une représentation sur 8 bits ici), les nombres impairs ont 1 à la fin, les nombres pairs ont 0.

Par exemple:

1 = 00000001

2 = 00000010

3 = 00000011

4 = 00000100

Si vous prenez un nombre et utilisez ET au niveau du bit (& en java) par 1, il retournera soit 00000001, = 1, ce qui signifie que le nombre est impair. Ou 00000000 = 0, ce qui signifie que le nombre est pair.

Par exemple

Est impair?

1 & 1 =

00000001 &

00000001 =

00000001 <- Impair

2 & 1 =

00000010 &

00000001 =

00000000 <- Pair

54 & 1 =

00000001 &

00110110 =

00000000 <- Pair

C'est pourquoi cela fonctionne:

if(number & 1){

   //Number is odd

} else {

   //Number is even
}

Désolé si c'est redondant.


1

Parité zéro | zéro http://tinyurl.com/oexhr3k

Séquence de code Python.

# defining function for number parity check
def parity(number):
    """Parity check function"""
    # if number is 0 (zero) return 'Zero neither ODD nor EVEN',
    # otherwise number&1, checking last bit, if 0, then EVEN, 
    # if 1, then ODD.
    return (number == 0 and 'Zero neither ODD nor EVEN') \
            or (number&1 and 'ODD' or 'EVEN')

# cycle trough numbers from 0 to 13 
for number in range(0, 14):
    print "{0:>4} : {0:08b} : {1:}".format(number, parity(number))

Production:

   0 : 00000000 : Zero neither ODD nor EVEN
   1 : 00000001 : ODD
   2 : 00000010 : EVEN
   3 : 00000011 : ODD
   4 : 00000100 : EVEN
   5 : 00000101 : ODD
   6 : 00000110 : EVEN
   7 : 00000111 : ODD
   8 : 00001000 : EVEN
   9 : 00001001 : ODD
  10 : 00001010 : EVEN
  11 : 00001011 : ODD
  12 : 00001100 : EVEN
  13 : 00001101 : ODD

@ el.pescado, merci. Si Zero est pair, combien de paires a-t-il?

@ el.pescado, Ok, je suis d'accord avec toi. Alors, si réfléchissez un peu, pourquoi nous divisons en 2 (deux)? Ce que nous voulons savoir, lorsque nous nous divisons en deux? Pourquoi ne pas diviser en 3, ou 5, etc.?

@ el.pescado Cet article Wikipedia Parity of Zero est faux. Beaucoup de gens ont été trompés par cet article. Réfléchissez avant Wink.

1
Vous avez raison. Maintenant que j'ai lu d'autres réponses, j'ai trouvé la vôtre la plus complète :)
el.pescado

@ el.pescado. Merci. :) Maintenant tu es le meilleur ami de Zero. (câlin)

1
I execute this code for ODD & EVEN:

#include <stdio.h>
int main()
{
    int number;
    printf("Enter an integer: ");
    scanf("%d", &number);

    if(number % 2 == 0)
        printf("%d is even.", number);
    else
        printf("%d is odd.", number);
}

0

Par souci de discussion ...

Il vous suffit de regarder le dernier chiffre d'un nombre donné pour voir s'il est pair ou impair. Signé, non signé, positif, négatif - ils sont tous les mêmes à ce sujet. Donc, cela devrait fonctionner tout autour: -

void tellMeIfItIsAnOddNumberPlease(int iToTest){
  int iLastDigit;
  iLastDigit = iToTest - (iToTest / 10 * 10);
  if (iLastDigit % 2 == 0){
    printf("The number %d is even!\n", iToTest);
  } else {
    printf("The number %d is odd!\n", iToTest);
  }
}

La clé ici est dans la troisième ligne de code, l'opérateur de division effectue une division entière, de sorte que le résultat manque la partie fraction du résultat. Ainsi, par exemple, 222/10 donnera 22 comme résultat. Ensuite, multipliez-le à nouveau avec 10 et vous avez 220. Soustrayez cela de l'original 222 et vous vous retrouvez avec 2, qui par magie est le même nombre que le dernier chiffre du nombre d'origine. ;-) Les parenthèses sont là pour nous rappeler l'ordre dans lequel le calcul est effectué. Effectuez d'abord la division et la multiplication, puis soustrayez le résultat du nombre d'origine. Nous pourrions les laisser de côté, car la priorité est plus élevée pour la division et la multiplication que pour la soustraction, mais cela nous donne un code "plus lisible".

Nous pourrions tout rendre complètement illisible si nous le voulions. Cela ne ferait aucune différence pour un compilateur moderne: -

printf("%d%s\n",iToTest,0==(iToTest-iToTest/10*10)%2?" is even":" is odd");

Mais cela rendrait le code plus difficile à maintenir à l'avenir. Imaginez simplement que vous souhaitez changer le texte des nombres impairs en "n'est pas pair". Plus tard, quelqu'un d'autre voudra savoir quelles modifications vous avez apportées et effectuer un diff svn ou similaire ...

Si vous n'êtes pas préoccupé par la portabilité mais plutôt par la vitesse, vous pouvez jeter un œil au bit le moins significatif. Si ce bit est mis à 1 c'est un nombre impair, s'il est 0 c'est un nombre pair. Sur un petit système endian, comme l'architecture x86 d'Intel, ce serait quelque chose comme ceci: -

if (iToTest & 1) {
  // Even
} else {
  // Odd
}

Qu'est-ce qui ne va pas exactement avec iToTest% 2 == 0? Vous perdez une division en extrayant le dernier chiffre, la vôtre est donc deux fois plus lente qu'elle ne devrait l'être.
freespace

@freespace: je gaspille plus que ça, non? :-) Une multiplication et une soustraction aussi. Mais ce qui est le plus efficace entre les deux solutions, je n'ose pas le dire. Je n'ai jamais prétendu que c'était la solution la plus rapide, bien au contraire si vous relisez la première ligne de mon message.
Tooony

@Tooony, ah, mon chapeau d'humour est tombé. Il est officiellement de retour maintenant: D Désolé à ce sujet :)
freespace

0

Si vous voulez être efficace, utilisez des opérateurs au niveau du bit ( x & 1), mais si vous voulez être lisible, utilisez modulo 2 ( x % 2)


-1: Si vous voulez être efficace, utilisez l'un ou l'autre. Si vous voulez qu'il soit portable, utilisez %. Si vous voulez qu'il soit lisible, utilisez %. Hmmm, je vois un motif ici.
Thomas Eding

@trinithis, il n'y a pas de modèle et cette solution est bien meilleure que la vôtre.
Sous

0

Vérifier pair ou impair est une tâche simple.

Nous savons que tout nombre exactement divisible par 2 est un nombre pair impair.

Nous avons juste besoin de vérifier la divisibilité de n'importe quel nombre et pour vérifier la divisibilité, nous utilisons l' %opérateur

Vérifier même impair en utilisant sinon

if(num%2 ==0)  
{
    printf("Even");
}
else
{
    printf("Odd");
}

Programme C pour vérifier pair ou impair en utilisant sinon

Utilisation de l'opérateur conditionnel / ternaire

(num%2 ==0) printf("Even") : printf("Odd");

Programme C pour vérifier pair ou impair en utilisant l'opérateur conditionnel .

Utilisation de l'opérateur Bitwise

if(num & 1)  
{
    printf("Odd");
}
else 
{
    printf("Even");
}

et où est exactement l'opérateur ternaire?
Beyondo

0

+ 66% plus rapide>!(i%2) / i%2 == 0

int isOdd(int n)
{
    return n & 1;
}

Le code vérifie le dernier bit de l'entier s'il est 1 en binaire

Explication

Binary  :   Decimal
-------------------
0000    =   0
0001    =   1
0010    =   2
0011    =   3
0100    =   4
0101    =   5
0110    =   6
0111    =   7
1000    =   8
1001    =   9
and so on...

Notez que le bit le plus à droite est toujours 1 pour les nombres impairs .

les & contrôles de niveau du bit et le bit opérateur dans notre extrême droite retour ligne si c'est 1

Pensez-y comme vrai et faux

Lorsque nous comparons n avec 1, ce qui signifie 0001en binaire (le nombre de zéros n'a pas d'importance).
Imaginons alors que nous avons l'entier n avec une taille de 1 octet.

Il serait représenté par des chiffres 8 bits / 8 bits.

Si l'int n était 7 et que nous le comparons à 1 , c'est comme

7 (1-byte int)|    0  0  0  0    0  1  1  1
       &
1 (1-byte int)|    0  0  0  0    0  0  0  1
********************************************
Result        |    F  F  F  F    F  F  F  T

Quel F signifie faux et T pour vrai.

Il ne compare que le bit le plus à droite s'ils sont tous les deux vrais. Donc, automagiquement, 7 & 1c'est T rue.

Et si je veux vérifier le bit avant le plus à droite?

Changez simplement n & 1à n & 2quel 2 représente 0010en binaire et ainsi de suite.

Je suggère d'utiliser la notation hexadécimale si vous débutez dans les opérations au niveau du bit
return n & 1;>> return n & 0x01;.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.