web-dev-qa-db-fra.com

Déterminer si un entier signé et un entier non signé sont tous les deux pairs ou impairs

J'ai un int m Et un unsigned int j et veulent déterminer s'ils sont tous les deux pairs ou impairs.

Dans le passé, j'utilisais

if((int(j)+m)%2)

pour attraper le cas qu'un seul est étrange. Mais je suis préoccupé par le transtypage en int en modifiant de façon incorrecte l'impair-pair de j.

Est-ce que l'un ou l'autre d'entre eux rencontre des problèmes?

if(!(j%2)!=!(m%2))
if(bool(j%2)!=bool(j%2))

Je le sais

if(j%2!=m%2)

ne fonctionne pas car 'm% 2' produira -1 lorsque m est négatif, ce qui sera toujours évalué à true quelle que soit la valeur de j%2 est.

31
Johnathan Gross

N'utilisez pas %. C'est un problème qui appelle des masques de bit:

bool same_parity = (i & 0x1) == (j & 0x1);

Cela fonctionne quel que soit le signe de i, car le résultat de cette expression sera toujours 0 ou 1.

64
Barry
if (1 & (i ^ j))
{
// Getting here if i is even and j is odd
// or if i is odd and j is even
}

^ Est l'opérateur au niveau du bit exclusive-or, Qui vérifie chaque bit dans les deux nombres s'ils ont la même valeur. Par exemple, si la représentation binaire de i est 0101 Et j est 1100, Alors i ^ j Sera évalué à 1001, car leurs premier et dernier bits sont différents, tandis que les bits du milieu sont les mêmes.

& Est l'opérateur and au niveau du bit, qui vérifie chaque bit dans les deux nombres s'ils sont tous les deux 1.

Étant donné que seul le dernier bit de chaque nombre détermine s'il est pair ou impair, i ^ j Sera évalué à ...xxx0 S'ils sont tous les deux pairs ou impairs, et ...xxx1 Sinon (le xs n'ont pas d'importance, nous ne les regardons pas de toute façon). Puisque 1 Est en fait ...0001, 1 & (i ^ j) est évalué à 0 Si i et j sont tous les deux pairs ou impairs , et 1 sinon.

Cela fonctionne sur n'importe quelle combinaison de nombres non signés, de complément à 2 et de signe et de magnitude, mais pas sur le rare complément à 1 si exactement l'un est négatif.

56
SolutionMill

L'ajout de deux entiers ajoute leur parité, donc la solution est simplement:

if ( (j + m) % 2 )

L'enveloppement non signé ne perturbe pas cette propriété, car il est fait modulo UINT_MAX+1 qui est un nombre pair.

Cette solution ne dépend d'aucun détail spécifique à l'implémentation comme la représentation numérique négative.


Note de bas de page: J'ai du mal à voir pourquoi tant d'autres réponses sont déterminées à compliquer le problème avec des décalages de bits, des compléments de bits, des XOR, etc. etc. Malheureusement, IMO, il est parfois glorifié dans les communautés C ou C++ d'écrire code délicat au lieu de code simple.

20
M.M

Lancer un unsigned int supérieur à INT_MAX à int n'est pas garanti pour renvoyer une valeur sensible. Le résultat n'est pas défini.

Casting d'un int vers un unsigned int entraîne toujours un comportement défini - il fait des calculs mod 2^k pour certains k suffisamment grand pour que chaque int positif soit inférieur à 2^k.

if((int(j)+m)%2)

devrait être

if((j+unsigned(m))%2)

au lieu.

if((j%2)==(unsigned(m)%2))

est le moyen le plus simple de voir si les deux ont la même parité. Passer à un non signé aka mod 2^k va maintenir la parité, et en non signé %2 renvoie la parité correctement (et non une parité négative).

16

Ne sois pas trop intelligent

Est-ce que l'un ou l'autre d'entre eux rencontre des problèmes?

if(!(j%2)!=!(m%2))
if(bool(j%2)!=bool(j%2))

Un problème que je vois est la lisibilité. Il pourrait ne pas être évident pour quelqu'un d'autre (ou votre futur moi) ce qu'il est censé faire ou ce qu'il fait réellement.

Vous pourriez être plus expressif en dépensant quelques lignes supplémentaires:

#include <cmath>

const bool fooIsEven = foo % 2 == 0;
const bool barIsEven = std::abs(bar) % 2 == 0;
if (fooIsEven == barIsEven)
{
  // ...
}

Pensez également à implémenter une fonction correctement nommée qui fournit une comparaison de la parité de deux types intégraux donnés. Cela nettoie non seulement votre code mais vous empêche également de vous répéter.

Edit: Cast remplacé par appel à std :: abs

6
plats1