代码之家  ›  专栏  ›  技术社区  ›  Vivek Sharma

如何在不使用+或+或其他算术运算符的情况下添加两个数字

  •  52
  • Vivek Sharma  · 技术社区  · 17 年前

    如何在不使用+或+或任何其他算术运算符的情况下添加两个数字?

    这是很久以前在校园采访中问的一个问题。不管怎样,今天有人问了一个关于比特操作的问题,并回答了一句漂亮的妙语。 Stanford bit twiddling 被提及。我花了一些时间研究它,并认为事实上可能有一个问题的答案。我不知道,我找不到。是否存在答案?

    20 回复  |  直到 9 年前
        1
  •  95
  •   Peter Mortensen Pieter Jan Bonestroo    15 年前

    这是我不久前为好玩写的东西。它使用了 two's complement 使用带进位的重复移位表示和实现加法,主要实现加法方面的其他运算符。

    #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;
    }
    
        2
  •  51
  •   Tom Leys    17 年前

    或者,与Jason的按位方法不同,您可以并行计算许多位—对于大数字,这应该运行得更快。在每个步骤中,计算出执行部分和求和部分。您试图将进位添加到和中,这可能导致再次进位-因此是循环。

    >>> 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
    

    当你加1和3时,两个数字都有1位的集合,所以1+1的和进位。下一步你加2到2,这等于正确的和4。会导致出口

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

    或者更复杂的例子

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

    编辑: 为了使它能很容易地处理有符号的数字,您需要在a和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
    

    试穿

    add(-1, 1)
    

    看到一个位在整个范围内进位并溢出32次迭代

    4294967294 2
    4294967292 4
    4294967288 8
    ...
    4294901760 65536
    ...
    2147483648 2147483648
    0 0
    0L
    
        3
  •  20
  •   Peter Mortensen Pieter Jan Bonestroo    15 年前
    int Add(int a, int b)
    {
        while (b)
        {
            int carry = a & b;
            a = a ^ b;
            b = carry << 1;
        }
        return a;
    }
    
        4
  •  17
  •   Samuel Carrijo    17 年前

    你可以改变 adder circuit 变成一个算法。它们只执行位运算=)

        5
  •  7
  •   Fabio Ceconello    17 年前

    好吧,用布尔运算符实现等价物是非常简单的:使用进位(和)进行逐位求和(XOR)。这样地:

    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;
    }
    
        6
  •  6
  •   rampion    17 年前

    你已经得到了一些操纵的答案。这里有一些不同的东西。

    在C中, arr[ind] == *(arr + ind) . 这让我们做一些有点困惑(但合法)的事情,比如 int arr = { 3, 1, 4, 5 }; int val = 0[arr]; .

    因此,我们可以定义一个自定义的添加函数(不显式使用算术运算符),从而:

    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]);
    }
    

    或者,如果我们想避免这个技巧,并且通过算术运算符,它们包括 | , & 和 ^ (因此不允许直接进行位操作),我们可以通过查找表进行:

    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;
    }
    
        7
  •  5
  •   Peter Mortensen Pieter Jan Bonestroo    15 年前

    所有的算术运算都分解成位运算,在电子设备中使用与非门和或等门来实现。

    Adder composition can be seen here .

        8
  •  5
  •   RiaD    14 年前

    对于无符号数,使用与第一节课中学习的相同的加法算法,但对于基数2而不是基数10。例如,3+2(基10),即基2中的11+10:

       1         ‹--- carry bit
       0 1 1     ‹--- first operand (3)
     + 0 1 0     ‹--- second operand (2)
     -------
       1 0 1     ‹--- total sum (calculated in three steps)
    
        9
  •  4
  •   ChrisV    17 年前

    如果你觉得有喜剧性,总是有这种惊人的可怕的方法来添加两个(相对较小的)无符号整数。代码中没有算术运算符。

    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;
    }
    

    在C中,使用stdio(在Microsoft编译器上用_snprintf替换snprintf):

    #include <stdio.h>
    unsigned int JokeAdder(unsigned int a, unsigned int b)
    {
        return snprintf(NULL, 0, "%*.*s%*.*s", a, a, "", b, b, "");
    }
    
        10
  •  3
  •   Tarik Kaya    11 年前

    这是一个紧凑的C解决方案。有时递归比循环更可读。

    int add(int a, int b){
        if (b == 0) return a;
        return add(a ^ b, (a & b) << 1);
    }
    
        11
  •  1
  •   thkala jaxb    15 年前
    #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;
    }
    
        12
  •  1
  •   Peter Mortensen Pieter Jan Bonestroo    15 年前
    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;
    }
    
        13
  •  1
  •   Tom Leys    13 年前
    ## 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();
    }
    
        14
  •  0
  •   Peter Mortensen Pieter Jan Bonestroo    15 年前

    以下是可行的。

    x - (-y)
    
        15
  •  0
  •   herohuyongtao    12 年前

    这可以递归完成:

    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
    }
    

    或迭代地:

    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;
    }
    
        16
  •  0
  •   APC    10 年前

    不使用的实现加法、乘法的代码 + , * 操作员; 对于减法,将数字的1的补码+1传递给 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;
    }
    
        17
  •  0
  •   Box Box Box Box    10 年前

    这个问题问如何加上两个数字,所以我不明白为什么所有的解决方案都提供两个整数的加法?如果这两个数字是浮点数,即 2.3 + 1.8 它们也不算数字吗?要么问题需要修改,要么答案需要修改。

    对于浮点数,我认为应该将数字分解为其组成部分,即 2.3 = 2 + 0.3 然后 0.3 应转换为整数表示形式,并乘以其指数因子,即 0.3 = 3 * 10^-1 对另一个数字做同样的操作,然后使用一个位移位方法添加整数段,作为上述处理情况的解决方案,将其结转到单位数字位置,即。 2.7 + 3.3 = 6.0 = 2+3+0.7+0.3 = 2 + 3 + 7x10^-1 + 3x10^-1 = 2 + 3 + 10^10^-1 (这可以作为两个单独的附加项处理 2+3=5 然后 5+1=6 )

        18
  •  -1
  •   Tom    11 年前
    int add_without_arithmatic(int a, int b)
    {
        int sum;
        char *p;
        p = (char *)a;
        sum = (int)&p[b];
        printf("\nSum : %d",sum);
    }
    
        19
  •  -1
  •   Kuuo    9 年前

    有了上面给出的答案,可以用单行代码完成:

    int add(int a, int b) {
        return (b == 0) ? a : add(a ^ b, (a & b) << 1);
    }
    
        20
  •  -1
  •   marcadian    9 年前

    我认为这段代码对于不带加号运算符的两个数字的加法有帮助

    #include<stdio.h>
    
    int main()
    
    {
        int a, b, c;
    
        printf("enter two no. : ");
        scanf("%d%d", &a, &b);
    
        c = (a - ~b - 1);
        printf("%d\n", c);
    
        return 0;
    }