68 votes

Est-il possible d'implémenter des opérateurs au niveau du bit en utilisant l'arithmétique entière?

Je suis plutôt face à un problème particulier. Je suis en train de travailler sur un compilateur pour une architecture qui ne prend pas en charge les opérations bit à bit. Cependant, il gère signé de 16 bits en entier de l'arithmétique et je me demandais si il serait possible de mettre en œuvre des opérations bit à bit en utilisant uniquement:

  • Outre (c = a + b)
  • La soustraction (c = a - b)
  • Division (c = a / b)
  • La Multiplication (c = a * b)
  • Module (c = a % b)
  • Minimum (c = min(a, b))
  • Maximum (c = max(a, b))
  • Les comparaisons (c = (a < b), c = (a == b), c = (a <= b), et.c.)
  • Sauts (goto, de, he.c.)

Les opérations bit à bit je veux être en mesure de soutien sont:

  • Ou (c = a | b)
  • Et (c = a & b)
  • Xor (c = a ^ b)
  • Décalage à gauche (c = a << b)
  • Décalage à droite (c = a >> b)
    • (Tous les nombres entiers sont signés, donc c'est un problème)
  • Signé Maj (c = a >>> b)
  • Complément (a = ~b)
    • (Déjà trouvé une solution, voir ci-dessous)

Normalement le problème est dans l'autre sens; comment obtenir de l'arithmétique des optimisations au niveau du bit à l'aide de hacks. Cependant pas dans ce cas.

Écriture de la mémoire est très rare sur cette architecture, d'où la nécessité pour les opérations bit à bit. Les fonctions au niveau du bit eux-mêmes ne devraient pas utiliser beaucoup de variables temporaires. Cependant, la constante de données en lecture seule & l'instruction de la mémoire est abondante. Une note de côté ici aussi c'est que les sauts et les branches ne sont pas cher et toutes les données sont facilement mis en cache. Sauts coût de la moitié des cycles que l'arithmétique (y compris les load/store) instructions de le faire. En d'autres termes, toutes ces fonctions prises en charge des coûts deux fois les cycles d'un seul saut.

Quelques idées qui pourraient vous aider:


J'ai compris que vous pouvez faire un complément (négation bits) avec le code suivant:

// Bitwise one's complement
b = ~a;
// Arithmetic one's complement
b = -1 - a;

Je me souviens aussi de l'ancienne maj hack lors de la division avec une puissance de deux de sorte que le bit à bit shift peut être exprimé comme:

// Bitwise left shift
b = a << 4;
// Arithmetic left shift
b = a * 16; // 2^4 = 16

// Signed right shift
b = a >>> 4;
// Arithmetic right shift
b = a / 16;

Pour le reste des opérations bit à bit, je suis un peu paumé. Je souhaite que les architectes de cette architecture ont fourni peu d'opérations.

Je voudrais aussi savoir si il est rapide/moyen facile de calculer la puissance de deux (pour les opérations de déplacement) sans l'aide d'une mémoire de données de la table. Un naïf solution serait de sauter dans un champ de multiplications:

b = 1;
switch (a)
{
  case 15: b = b * 2;
  case 14: b = b * 2;
  // ... exploting fallthrough (instruction memory is magnitudes larger)
  case 2: b = b * 2;
  case 1: b = b * 2;
}

Ou d'un Ensemble & Jump approche:

switch (a)
{
  case 15: b = 32768; break;
  case 14: b = 16384; break;
  // ... exploiting the fact that a jump is faster than one additional mul
  //     at the cost of doubling the instruction memory footprint.
  case 2: b = 4; break;
  case 1: b = 2; break;
}

31voto

Durandal Points 9434

La première des solutions pour le décalage (shift est le changement de la distance, ne doit pas être négative, est l'opérande doit être décalé et contient aussi le résultat quand fait). La puissance de la table est utilisée par tous les trois opérations de décalage.

// table used for shift operations
powtab = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, -32768 };

// logical shift left
if (shift > 15) {
     a = 0; // if shifting more than 15 bits to the left, value is always zero
} else {
     a *= powtab[shift];
}

// logical shift right (unsigned)
if (shift > 15) {
    a = 0; // more than 15, becomes zero
} else if (shift > 0) {
    if (a < 0) {
        // deal with the sign bit (15)
        a += -32768;
        a /= powtab[shift];
        a += powtab[15 - shift];
    } else {
        a /= powtab[shift];
    }
}

// arithmetic shift right (signed)
if (shift >= 15) {
    if (a < 0) {
        a = -1;
    } else {
        a = 0;
    }
} else if (shift > 0) {
    if (a < 0) {
        // deal with the sign bit
        a += -32768;
        a /= powtab[shift];
        a -= powtab[15 - shift];
    } else {
        // same as unsigned shift
        a /= powtab[shift];
    }
}

ET, OU et ou exclusif (XOR), je ne pouvais pas venir avec une solution simple, donc je vais le faire avec le boucler sur chaque bit unique. Il y a peut-être un meilleur truc à faire. Pseudocode suppose a et b sont des opérandes d'entrée, c est le résultat de la valeur, x est le compteur de la boucle (chaque boucle doit exécuter exactement 16 fois):

// XOR (^)
c = 0;
for (x = 0; x <= 15; ++x) {
    c += c;
    if (a < 0) {
        if (b >= 0) {
            c += 1;
        }
    } else if (b < 0) {
        c += 1;
    }
    a += a;
    b += b;
}

// AND (&)
c = 0;
for (x = 0; x <= 15; ++x) {
    c += c;
    if (a < 0) {
        if (b < 0) {
            c += 1;
        }
    }
    a += a;
    b += b;
}

// OR (|)
c = 0;
for (x = 0; x <= 15; ++x) {
    c += c;
    if (a < 0) {
        c += 1;
    } else if (b < 0) {
        c += 1;
    }
    a += a;
    b += b;
}

C'est en supposant que toutes les variables sont en 16 bits et toutes les opérations se comporter comme signé (si a<0 qui est effectivement vrai lorsque le bit 15 est réglé).

EDIT: en fait j'ai testé tout est possible opérande valeurs (-32768 à 32767) pour les quarts allant de 0 à 31 pour l'exactitude et elle fonctionne correctement (en supposant entier divise). Pour le ET/OU/XOR code un test exhaustif prend trop de temps sur ma machine, mais depuis que le code est assez simple, il devrait y avoir aucune arête cas de toute façon.

6voto

Joshua Points 13231

Dans cet environnement, il serait préférable de configurer pour utiliser réellement des opérateurs arithmatiques pour décoller les composants d'entiers.

PAR EXEMPLE

 if (a & 16)  becomes if ((a % 32) > 15)
a &= 16 becomes if ((a % 32) < 15) a += 16
 

Les transformations de ces opérateurs sont suffisamment évidentes si vous limitez RHS à une puissance constante de 2.

Il est également facile de décoller deux ou quatre bits.

3voto

SigTerm Points 16055

Vous pouvez utiliser bit-à-bit (comme Mark l'A suggéré), par extracing tous les bits qui sera lente.

Ou vous pouvez accélérer le processus et l'utilisation 2d tables qui stockent des résultats, disons, pour deux 4 bits des opérandes et opèrent sur ces. Vous aurez besoin de moins d'extractions que si vous étiez d'exploitation sur les bits.

Vous pouvez également faire tout en utilisant addition, soustraction et >= opération. Chaque opération au niveau du bit peut être déroulé en quelque chose comme ceci à l'aide de macros:

/*I didn't actually compile/test it, it is just illustration for the idea*/
uint16 and(uint16 a, uint16 b){
    uint16 result = 0;
    #define AND_MACRO(c) \
        if (a >= c){ \ 
            if (b >= c){\
                result += c;\
                b -= c;\
            }\
            a -= c;\
        }\
        else if (b >= c)\
            b -= c;

    AND_MACRO(0x8000)
    AND_MACRO(0x4000)
    AND_MACRO(0x2000)
    AND_MACRO(0x1000)
    AND_MACRO(0x0800)
    AND_MACRO(0x0400)
    AND_MACRO(0x0200)
    AND_MACRO(0x0100)
    AND_MACRO(0x0080)
    AND_MACRO(0x0040)
    AND_MACRO(0x0020)
    AND_MACRO(0x0010)
    AND_MACRO(0x0008)
    AND_MACRO(0x0004)
    AND_MACRO(0x0002)
    AND_MACRO(0x0001)
    #undef AND_MACRO
    return result;
}

Vous aurez besoin de 3 variables pour mettre en œuvre cette.

Chaque opération au niveau du bit s'articuleront autour des macros similaire à AND_MACRO - vous de comparer les valeurs restantes de a et b pour le "masque" (qui est "c" paramètre). puis ajouter un masque pour le résultat dans le cas de la branche qui est approprié pour votre opération. Et vous soustrayez le masque de valeurs, si le bit est défini.

En fonction de votre plate-forme, il peut être plus rapide que l'extraction de tous les bits à l'aide de % et de / , puis de le remettre à l'aide de la multiplication.

Voyez par vous-même selon ce qui est le mieux pour vous.

2voto

tpdi Points 18427

Aussi longtemps que vous êtes prêt pour être très cher, oui.

Fondamentalement, vous aurez explicitement de mettre un nombre en base 2 de la représentation. Vous faites cela, comme vous le feriez mettre un nombre en base 10 (par exemple, pour l'imprimer), c'est par la répétition de la division.

Cela transforme votre nombre dans un tableau de booléens (ou ints dans la gamme 0,1), alors nous ajouter des fonctions à opérer sur ces tableaux.

encore une fois, pas que c'est énormément plus cher que les opérations bit à bit, et que presque toute l'architecture de l'approvisionnement des opérateurs au niveau du bit.

Dans C (bien sûr, en C, vous avez opérateurs au niveau du bit, mais...) une mise en œuvre pourrait être:

include <limits.h>
const int BITWIDTH = CHAR_BIT;
typedef int[BITWIDTH] bitpattern;

// fill bitpattern with base-2 representation of n
// we used an lsb-first (little-endian) representation
void base2(char n, bitpattern array) {
  for( int i = 0 ; i < BITWIDTH ; ++i ) {
    array[i] = n % 2 ;
    n /= 2 ;
  }
}

void bitand( bitpattern op1, bitpattern op2, bitpattern result ) {
  for( int i = 0 ; i < BITWIDTH ; ++i ) {
    result[i] = op1[i] * op2[i];
  }
}


void bitor( bitpattern op1, bitpattern op2, bitpattern result ) {
  for( int i = 0 ; i < BITWIDTH ; ++i ) {
    result[i] = (op1[i] + op2[i] != 0 );
  }
}

// assumes compiler-supplied bool to int conversion 
void bitxor( bitpattern op1, bitpattern op2, bitpattern result ) {
  for( int i = 0 ; i < BITWIDTH ; ++i ) {
    result[i] = op1[i] != op2[i]  ;
  }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X