web-dev-qa-db-fra.com

Vérifiez si un numéro est non nul en utilisant des opérateurs bits dans C

Vérifiez si un numéro x est non nulleo à l'aide des opérateurs juridiques sauf !.

Exemples: isNonZero(3) = 1, isNonZero(0) = 0

OPS juridique: ~&^|+<<>>

  • Remarque: Seuls les opérateurs bitwises doivent être utilisés. if, else, for, etc. ne peut pas être utilisé.
  • EDIT1: Nombre d'opérateurs ne doit pas dépasser 10.
  • EDIT2: Pensez à la taille de int pour être 4 octets.
int isNonZero(int x) {
return ???;
}

En utilisant ! Ce serait trivial, mais comment le faisons-nous sans utiliser !?

25
Eternal Learner

La version logarithmique de la fonction Adamk:

int isNotZero(unsigned int n){
  n |= n >> 16;
  n |= n >> 8;
  n |= n >> 4;
  n |= n >> 2;
  n |= n >> 1;
  return n & 1;
};

Et le plus rapide, mais en montage:

xor eax, eax
sub eax, n  // carry would be set if the number was not 0
xor eax, eax
adc eax, 0  // eax was 0, and if we had carry, it will became 1

Quelque chose de similaire à la version de montage peut être écrit en C, il vous suffit de jouer avec le pancarte et avec quelques différences.

EDIT: Voici la version la plus rapide que je puisse penser en C:

1) Pour les nombres négatifs: si le bit de signe est défini, le nombre n'est pas 0.

2) pour positif: 0 - n sera négatif et peut être vérifié comme dans le cas 1. Je ne vois pas le - Dans la liste des opérations légales, nous allons donc utiliser ~n + 1 au lieu.

Ce que nous obtenons:

int isNotZero(unsigned int n){ // unsigned is safer for bit operations
   return ((n | (~n + 1)) >> 31) & 1;
}
41
ruslik
int isNonZero(unsigned x) {
    return ~( ~x & ( x + ~0 ) ) >> 31;
}

En supposant que INT est 32 bits (/ * Edit: cette partie ne s'applique plus que j'ai modifié le type de paramètre sur Unsigné */et que les changements signés se comportent exactement comme non signé).

11
usta

Pourquoi faire compliquer les choses?

int isNonZero(int x) {
    return x;
}

Cela fonctionne parce que la Convention C est que chaque valeur non nulle signifie vrai, comme Isnonzero renvoie un Int qui est légal.

Certaines personnes ont fait valoir que la fonction isnonzero () doit renvoyer 1 pour l'entrée 3, comme indiqué dans l'exemple.

Si vous utilisez C++, il est toujours aussi facile qu'avant:

int isNonZero(int x) {
    return (bool)x;
}

Maintenant, la fonction renvoie 1 si vous fournissez 3.

Ok, cela ne fonctionne pas avec C qui manque un type booléen approprié.

Maintenant, si vous supposez que les INT sont 32 bits et + est autorisé:

int isNonZero(int x) {
    return ((x|(x+0x7FFFFFFF))>>31)&1;
}

Sur certaines architectures, vous pouvez même éviter la finale &1, juste en casting x à non signé (qui a un coût d'exécution NULL), mais qui est un comportement non défini, dépend donc de la mise en œuvre (dépend si l'architecture cible utilise la droite ou la suite logique).

int isNonZero(int x) {
    return ((unsigned)(x|(x+0x7FFFFFFF)))>>31;
}
10
kriss
int is_32bit_zero( int x ) {
    return 1 ^ (unsigned) ( x + ~0 & ~x ) >> 31;
}
  1. Soustrayez 1. (~0 génère moins un sur une machine de complément à deux. C'est une hypothèse.)
  2. Sélectionnez seulement un bit basculé qui s'est retourné à un.
  3. La plupart des bits significatifs ne se retourne que pour soustraire un si x is zéro.
  4. Déplacez le bit le plus significatif au moins significatif.

Je compte six opérateurs. Je pourrais utiliser 0xFFFFFFFF pour cinq. Le casting à unsigned ne compte pas sur une machine de complément de deux; v).

http://ideone.com/omobw

2
Potatoswatter

Bitwise OR Tous les bits dans le numéro:

int isByteNonZero(int x) {
    return ((x >> 7) & 1) |
           ((x >> 6) & 1) |
           ((x >> 5) & 1) |
           ((x >> 4) & 1) |
           ((x >> 3) & 1) |
           ((x >> 2) & 1) |
           ((x >> 1) & 1) |
           ((x >> 0) & 1);
}

int isNonZero(int x) {
  return isByteNonZero( x >> 24 & 0xff ) |
         isByteNonZero( x >> 16 & 0xff ) |
         isByteNonZero( x >> 8  & 0xff ) |
         isByteNonZero( x       & 0xff );
}
1
adamk

fondamentalement, vous devez ou les bits. Par exemple, si vous savez que votre numéro est de 8 bits de large:

int isNonZero(uint8_t x)
{
    int res = 0;
    res |= (x >> 0) & 1;
    res |= (x >> 1) & 1;
    res |= (x >> 2) & 1;
    res |= (x >> 3) & 1;
    res |= (x >> 4) & 1;
    res |= (x >> 5) & 1;
    res |= (x >> 6) & 1;
    res |= (x >> 7) & 1;

    return res;
}
0
Nathan Fellman