web-dev-qa-db-ja.com

++または+または別の算術演算子を使用せずに2つの数値を追加する方法

++または+またはその他の算術演算子を使用せずに2つの数値を追加するにはどうすればよいですか?

それはあるキャンパスのインタビューでずっと前に聞かれた質問でした。とにかく、今日誰かがいくつかのビット操作に関する質問をし、その答えに美しいクイッドスタンフォードビットをいじるが言及されました。私はそれを研究するのにいくらか時間を費やし、実際に質問への答えがあるかもしれないと考えました。知りません、見つけられませんでした。答えはありますか?

52
Vivek Sharma

これは楽しみのために少し前に書いたものです。 2の補数 表現を使用し、キャリービットで繰り返しシフトを使用して加算を実装し、主に加算に関して他の演算子を実装します。

#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

または、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
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

加算回路 をアルゴリズムに変換できます。彼らはビット単位の操作のみを行います=)

17
Samuel Carrijo

ブール演算子を使用して同等のものを実装するのは非常に簡単です。ビットごとの合計(XOR)とキャリー(AND)を実行します。このような:

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

あなたはすでにいくつかの操作の答えを得ています。ここに何か違うものがあります。

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

符号なしの数値については、ファーストクラスで学習したのと同じ加算アルゴリズムを使用しますが、ベース10ではなくベース2を使用します。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)
5
Frederico

すべての算術演算はビット演算に分解され、NAND、AND、ORなどのゲートを使用して電子機器に実装されます。

加算器の構成はこちらで確認できます

5
Indy9000

コメディーを感じているなら、2つの(比較的小さな)符号なし整数を追加するためのこの驚くほどひどいアプローチが常にあります。コードのどこにも算術演算子はありません。

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, "");
}
4
ChrisV

これがコンパクトなCソリューションです。再帰はループより読みやすい場合があります。

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

質問では、2つの数値を追加する方法を尋ねているため、すべてのソリューションで2つの整数を追加できる理由がわかりません。 2つの数値が浮動小数点数だった場合、つまり_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つの別個の加算_2+3=5_とその後_5+1=6_)

0
Tikky

これは再帰的に実行できます。

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

+*演算子を使用せずにadd、multiplicationを実装するコード。減算の場合、数値の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;
}
0
Anshul garg

次のように動作します。

x - (-y)
0
keraba