web-dev-qa-db-fra.com

Comment ajouter deux nombres sans utiliser ++ ou + ou un autre opérateur arithmétique

Comment ajouter deux nombres sans utiliser ++ ou + ou tout autre opérateur arithmétique?

C'était une question posée il y a longtemps dans une interview sur le campus. Quoi qu'il en soit, aujourd'hui, quelqu'un a posé une question concernant certaines manipulations de bits, et dans les réponses, une belle quide Stanford bit twiddling a été renvoyée. J'ai passé un peu de temps à l'étudier et j'ai pensé qu'il pourrait y avoir une réponse à la question. Je ne sais pas, je n'ai pas pu en trouver un. Existe-t-il une réponse?

52
Vivek Sharma

C'est quelque chose que j'ai écrit il y a quelque temps pour le plaisir. Il utilise une représentation complément à deux et implémente l'addition en utilisant des décalages répétés avec un bit de retenue, mettant en œuvre d'autres opérateurs principalement en termes d'addition.

#include <stdlib.h> /* atoi() */
#include <stdio.h>  /* (f)printf */
#include <assert.h> /* assert() */

int add(int x, int y) {
    int carry = 0;
    int result = 0;
    int i;

    for(i = 0; i < 32; ++i) {
        int a = (x >> i) & 1;
        int b = (y >> i) & 1;
        result |= ((a ^ b) ^ carry) << i;
        carry = (a & b) | (b & carry) | (carry & a);
    }

    return result;
}

int negate(int x) {
    return add(~x, 1);
}

int subtract(int x, int y) {
    return add(x, negate(y));
}

int is_even(int n) {
    return !(n & 1);
}

int divide_by_two(int n) {
    return n >> 1;
}

int multiply_by_two(int n) {
    return n << 1;
}

int multiply(int x, int y) {
    int result = 0;

    if(x < 0 && y < 0) {
        return multiply(negate(x), negate(y));
    }

    if(x >= 0 && y < 0) {
        return multiply(y, x);
    }

    while(y > 0) {
        if(is_even(y)) {
            x = multiply_by_two(x);
            y = divide_by_two(y);
        } else {
            result = add(result, x);
            y = add(y, -1);
        }
    }

    return result;
}

int main(int argc, char **argv) {
    int from = -100, to = 100;
    int i, j;

    for(i = from; i <= to; ++i) {
        assert(0 - i == negate(i));
        assert(((i % 2) == 0) == is_even(i));
        assert(i * 2 == multiply_by_two(i));
        if(is_even(i)) {
            assert(i / 2 == divide_by_two(i));
        }
    }

    for(i = from; i <= to; ++i) {
        for(j = from; j <= to; ++j) {
            assert(i + j == add(i, j));
            assert(i - j == subtract(i, j));
            assert(i * j == multiply(i, j));
        }
    }

    return 0;
}
96
Jason Creighton

Ou, plutôt que l'approche binaire de Jason, vous pouvez calculer plusieurs bits en parallèle - cela devrait fonctionner beaucoup plus rapidement avec de grands nombres. À chaque étape, déterminez la partie de transport et la partie qui est la somme. Vous essayez d'ajouter le report à la somme, ce qui pourrait entraîner à nouveau le report - d'où la boucle.

>>> def add(a, b):
    while a != 0:
        #      v carry portion| v sum portion
        a, b = ((a & b) << 1),  (a ^ b)
        print b, a
    return b

lorsque vous ajoutez 1 et 3, les deux nombres ont le bit 1 défini, donc la somme de ce 1 + 1 porte. L'étape suivante, vous ajoutez 2 à 2 et qui porte dans la bonne somme de quatre. Cela provoque une sortie

>>> add(1,3)
2 2
4 0
4

Ou un exemple plus complexe

>>> add(45, 291)
66 270
4 332
8 328
16 320
336

Edit: Pour que cela fonctionne facilement sur les numéros signés, vous devez introduire une limite supérieure sur a et b

>>> def add(a, b):
    while a != 0:
        #      v carry portion| v sum portion
        a, b = ((a & b) << 1),  (a ^ b)
        a &= 0xFFFFFFFF
        b &= 0xFFFFFFFF
        print b, a
    return b

Essaye le

add(-1, 1)

pour voir un seul bit se propager sur toute la plage et déborder sur 32 itérations

4294967294 2
4294967292 4
4294967288 8
...
4294901760 65536
...
2147483648 2147483648
0 0
0L
51
Tom Leys
int Add(int a, int b)
{
    while (b)
    {
        int carry = a & b;
        a = a ^ b;
        b = carry << 1;
    }
    return a;
}
20
intepid

Vous pouvez transformer un circuit additionneur en un algorithme. Ils ne font que des opérations au niveau du bit =)

17
Samuel Carrijo

Eh bien, implémenter un équivalent avec des opérateurs booléens est assez simple: vous faites une somme bit par bit (qui est un XOR), avec carry (qui est un AND). Comme ça:

int sum(int value1, int value2)
{
    int result = 0;
    int carry = 0;
    for (int mask = 1; mask != 0; mask <<= 1)
    {
        int bit1 = value1 & mask;
        int bit2 = value2 & mask;
        result |= mask & (carry ^ bit1 ^ bit2);
        carry = ((bit1 & bit2) | (bit1 & carry) | (bit2 & carry)) << 1;
    }
    return result;
}
7
Fabio Ceconello

Vous avez déjà obtenu quelques réponses de manipulation. Voici quelque chose de différent.

En C, arr[ind] == *(arr + ind). Cela nous permet de faire des choses légèrement confuses (mais légales) comme int arr = { 3, 1, 4, 5 }; int val = 0[arr];.

Nous pouvons donc définir une fonction d'ajout personnalisée (sans utilisation explicite d'un opérateur arithmétique) ainsi:

unsigned int add(unsigned int const a, unsigned int const b)
{
    /* this works b/c sizeof(char) == 1, by definition */
    char * const aPtr = (char *)a;
    return (int) &(aPtr[b]);
}

Alternativement, si nous voulons éviter cette astuce, et si par opérateur arithmétique ils incluent |, &, et ^ (donc la manipulation directe des bits n'est pas autorisée), nous pouvons le faire via la table de recherche:

typedef unsigned char byte;

const byte lut_add_mod_256[256][256] = { 
  { 0, 1, 2, /*...*/, 255 },
  { 1, 2, /*...*/, 255, 0 },
  { 2, /*...*/, 255, 0, 1 },
  /*...*/
  { 254, 255, 0, 1, /*...*/, 253 },
  { 255, 0, 1, /*...*/, 253, 254 },
}; 

const byte lut_add_carry_256[256][256] = {
  { 0, 0, 0, /*...*/, 0 },
  { 0, 0, /*...*/, 0, 1 },
  { 0, /*...*/, 0, 1, 1 },
  /*...*/
  { 0, 0, 1, /*...*/, 1 },
  { 0, 1, 1, /*...*/, 1 },
};

void add_byte(byte const a, byte const b, byte * const sum, byte * const carry)
{
  *sum = lut_add_mod_256[a][b];
  *carry = lut_add_carry_256[a][b];
}

unsigned int add(unsigned int a, unsigned int b)
{
  unsigned int sum;
  unsigned int carry;
  byte * const aBytes = (byte *) &a;
  byte * const bBytes = (byte *) &b;
  byte * const sumBytes = (byte *) &sum;
  byte * const carryBytes = (byte *) &carry;

  byte const test[4] = { 0x12, 0x34, 0x56, 0x78 };
  byte BYTE_0, BYTE_1, BYTE_2, BYTE_3;

  /* figure out endian-ness */
  if (0x12345678 == *(unsigned int *)test)
  {
    BYTE_0 = 3;
    BYTE_1 = 2;
    BYTE_2 = 1;
    BYTE_3 = 0;
  }
  else 
  {
    BYTE_0 = 0;
    BYTE_1 = 1;
    BYTE_2 = 2;
    BYTE_3 = 3;
  }


  /* assume 4 bytes to the unsigned int */
  add_byte(aBytes[BYTE_0], bBytes[BYTE_0], &sumBytes[BYTE_0], &carryBytes[BYTE_0]);

  add_byte(aBytes[BYTE_1], bBytes[BYTE_1], &sumBytes[BYTE_1], &carryBytes[BYTE_1]);
  if (carryBytes[BYTE_0] == 1)
  {
    if (sumBytes[BYTE_1] == 255)
    {
      sumBytes[BYTE_1] = 0;
      carryBytes[BYTE_1] = 1;
    }
    else
    {
      add_byte(sumBytes[BYTE_1], 1, &sumBytes[BYTE_1], &carryBytes[BYTE_0]);
    }
  }

  add_byte(aBytes[BYTE_2], bBytes[BYTE_2], &sumBytes[BYTE_2], &carryBytes[BYTE_2]);
  if (carryBytes[BYTE_1] == 1)
  {
    if (sumBytes[BYTE_2] == 255)
    {
      sumBytes[BYTE_2] = 0;
      carryBytes[BYTE_2] = 1;
    }
    else
    {
      add_byte(sumBytes[BYTE_2], 1, &sumBytes[BYTE_2], &carryBytes[BYTE_1]);
    }
  }

  add_byte(aBytes[BYTE_3], bBytes[BYTE_3], &sumBytes[BYTE_3], &carryBytes[BYTE_3]);
  if (carryBytes[BYTE_2] == 1)
  {
    if (sumBytes[BYTE_3] == 255)
    {
      sumBytes[BYTE_3] = 0;
      carryBytes[BYTE_3] = 1;
    }
    else
    {
      add_byte(sumBytes[BYTE_3], 1, &sumBytes[BYTE_3], &carryBytes[BYTE_2]);
    }
  }

  return sum;
}
6
rampion

Pour les nombres non signés, utilisez le même algorithme d'addition que celui que vous avez appris en première classe, mais pour la base 2 au lieu de la base 10. Exemple pour 3 + 2 (base 10), soit 11 + 10 en base 2:

   1         ‹--- carry bit
   0 1 1     ‹--- first operand (3)
 + 0 1 0     ‹--- second operand (2)
 -------
   1 0 1     ‹--- total sum (calculated in three steps)
5
Frederico

Toutes les opérations arithmétiques se décomposent en opérations au niveau du bit à implémenter en électronique, en utilisant des portes NAND, AND, OR, etc.

La composition de l'additionneur peut être vue ici .

5
Indy9000

Si vous vous sentez comique, il y a toujours cette approche terriblement horrible pour ajouter deux (relativement petits) entiers non signés. Aucun opérateur arithmétique n'importe où dans votre code.

En C #:

static uint JokeAdder(uint a, uint b)
{
    string result = string.Format(string.Format("{{0,{0}}}{{1,{1}}}", a, b), null, null);
    return result.Length;
}

En C, en utilisant stdio (remplacez snprintf par _snprintf sur les compilateurs Microsoft):

#include <stdio.h>
unsigned int JokeAdder(unsigned int a, unsigned int b)
{
    return snprintf(NULL, 0, "%*.*s%*.*s", a, a, "", b, b, "");
}
4
ChrisV

Voici une solution compacte en C. Parfois, la récursivité est plus lisible que les boucles.

int add(int a, int b){
    if (b == 0) return a;
    return add(a ^ b, (a & b) << 1);
}
3
Tarik Kaya
short int ripple_adder(short int a, short int b)
{
    short int i, c, s, ai, bi;

    c = s = 0;

    for (i=0; i<16; i++)
    {
        ai = a & 1;
        bi = b & 1;

        s |= (((ai ^ bi)^c) << i);
        c = (ai & bi) | (c & (ai ^ bi));

        a >>= 1;
        b >>= 1;
    }
    s |= (c << i);
    return s;
}
1
D P
## to add or subtract without using '+' and '-' ## 
#include<stdio.h>
#include<conio.h>
#include<process.h>

void main()
{
    int sub,a,b,carry,temp,c,d;

    clrscr();

    printf("enter a and b:");
    scanf("%d%d",&a,&b);

    c=a;
    d=b;
    while(b)
    {
        carry=a&b;
        a=a^b;
        b=carry<<1;
    }
    printf("add(%d,%d):%d\n",c,d,a);

    temp=~d+1;  //take 2's complement of b and add it with a
    sub=c+temp;
    printf("diff(%d,%d):%d\n",c,d,temp);
    getch();
}
1
Nitesh Pratap Singh
#include<stdio.h>

int add(int x, int y) {
    int a, b;
    do {
        a = x & y;
        b = x ^ y;
        x = a << 1;
        y = b;
    } while (a);
    return b;
}


int main( void ){
    printf( "2 + 3 = %d", add(2,3));
    return 0;
}
1
surya

La question demande comment ajouter deux nombres, donc je ne comprends pas pourquoi toutes les solutions proposent l'ajout de deux nombres entiers? Et si les deux nombres étaient des flottants, c'est-à-dire 2.3 + 1.8 ne sont-ils pas également considérés comme des nombres? Soit la question doit être révisée, soit les réponses.

Pour les flotteurs, je pense que les chiffres doivent être divisés en leurs composants, c'est-à-dire 2.3 = 2 + 0.3 puis le 0.3 doit être converti en une représentation entière en multipliant par son facteur exposant, c'est-à-dire 0.3 = 3 * 10^-1 faites de même pour l'autre nombre, puis ajoutez le segment entier en utilisant l'une des méthodes de décalage de bits données comme solution ci-dessus pour gérer les situations de report à l'emplacement des chiffres de l'unité, c'est-à-dire 2.7 + 3.3 = 6.0 = 2+3+0.7+0.3 = 2 + 3 + 7x10^-1 + 3x10^-1 = 2 + 3 + 10^10^-1 (cela peut être traité comme deux ajouts distincts 2+3=5 puis 5+1=6)

0
Tikky

Cela peut être fait récursivement:

int add_without_arithm_recursively(int a, int b)
{
    if (b == 0) 
        return a;

    int sum = a ^ b; // add without carrying
    int carry = (a & b) << 1; // carry, but don’t add
    return add_without_arithm_recursively(sum, carry); // recurse
}

ou itérativement:

int add_without_arithm_iteratively(int a, int b)
{
    int sum, carry;

    do 
    {
        sum = a ^ b; // add without carrying
        carry = (a & b) << 1; // carry, but don’t add

        a = sum;
        b = carry;
    } while (b != 0);

    return a;
}
0
herohuyongtao

Code pour implémenter l'ajout, la multiplication sans utiliser +, * opérateur; pour la soustraction, passez le complément 1 de +1 à la fonction add

#include<stdio.h>

unsigned int add(unsigned int x,unsigned int y)
{
         int carry=0;
    while (y != 0)
    {

        carry = x & y;  
        x = x ^ y; 
        y = carry << 1;
    }
    return x;
}
int multiply(int a,int b)
{
    int res=0;
    int i=0;
    int large= a>b ? a :b ;
    int small= a<b ? a :b ;
    for(i=0;i<small;i++)
    {
           res = add(large,res);                    
    }
    return res;
}
int main()
{
    printf("Sum :: %u,Multiply is :: %d",add(7,15),multiply(111,111));
    return 0;
}
0
Anshul garg

Ce qui suit fonctionnerait.

x - (-y)
0
keraba