web-dev-qa-db-ja.com

+演算子を使用せずに2つの数値を加算する最良の方法は何ですか?

友人と私は頭の体操を行ったり来たりしていますが、これを解決する方法がわかりません。私の仮定では、一部のビット演算子で可能ですが、確かではありません。

23
user23126

Cでは、ビット演算子を使用します。

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

XOR(x ^ y)キャリーなしの加算です。 (x & y)は各ビットからのキャリーアウトです。 (x & y) << 1は各ビットへのキャリーインです。

ループは、すべてのビットのキャリーがゼロになるまでキャリーを追加し続けます。

40
CMS
int add(int a, int b) {
   const char *c=0;
   return &(&c[a])[b];
}
22
ackb

いいえ+でしょ?

int add(int a, int b) 
{
   return -(-a) - (-b);
}
9
davidfowl

CMSのadd()関数は美しいです。単項否定(非ビット演算、加算を使用するのと同じ:-y ==(〜y)+1)によって汚されるべきではありません。したがって、同じビット単位のみの設計を使用した減算関数は次のとおりです。

int sub(int x, int y) {
    unsigned a, b;
    do {
        a = ~x & y;
        b =  x ^ y;
        x = b;
        y = a << 1;
    } while (a);
    return b;
}
5
Deadcode

「最良」を定義します。これがpythonバージョン:

len(range(x)+range(y))

+は、加算ではなくリストの連結を実行します。

5
Charlie Martin

これは、 ripple-carry adder と呼ばれる加算器の場合であり、機能しますが、最適には機能しないことに注意してください。ハードウェアに組み込まれているほとんどのバイナリ加算器は、 キャリー先見加算器 などの高速加算器の形式です。

私のリップルキャリー加算器は、carry_inを0に設定すると、符号なし整数と2の補数整数の両方で機能し、carry_inを1に設定すると、1の補数整数で機能します。また、加算時にアンダーフローまたはオーバーフローを示すフラグを追加しました。

#define BIT_LEN 32
#define ADD_OK 0
#define ADD_UNDERFLOW 1
#define ADD_OVERFLOW 2

int ripple_add(int a, int b, char carry_in, char* flags) {
    int result = 0;
    int current_bit_position = 0;
    char a_bit = 0, b_bit = 0, result_bit = 0;

    while ((a || b) && current_bit_position < BIT_LEN) {
        a_bit = a & 1;
        b_bit = b & 1;
        result_bit = (a_bit ^ b_bit ^ carry_in);
        result |= result_bit << current_bit_position++;
        carry_in = (a_bit & b_bit) | (a_bit & carry_in) | (b_bit & carry_in);
        a >>= 1;
        b >>= 1;
    }

    if (current_bit_position < BIT_LEN) {
        *flags = ADD_OK;
    }
    else if (a_bit & b_bit & ~result_bit) {
        *flags = ADD_UNDERFLOW;
    }
    else if (~a_bit & ~b_bit & result_bit) {
        *flags = ADD_OVERFLOW;
    }
    else {
        *flags = ADD_OK;
    }

    return result;
}
4
Lara Dougan

ビット演算子を使用したJavaソリューション:

// Recursive solution
public static int addR(int x, int y) {

    if (y == 0) return x;
    int sum = x ^ y; //SUM of two integer is X XOR Y
    int carry = (x & y) << 1;  //CARRY of two integer is X AND Y
    return addR(sum, carry);
}

//Iterative solution
public static int addI(int x, int y) {

    while (y != 0) {
        int carry = (x & y); //CARRY is AND of two bits
        x = x ^ y; //SUM of two bits is X XOR Y
        y = carry << 1; //shifts carry to 1 bit to calculate sum
    }
    return x;
}
4
realPK

チート。あなたは数を否定し、最初からそれを引くことができます:)

それができない場合は、バイナリ加算器がどのように機能するかを調べてください。 :)

編集:ああ、私が投稿した後にあなたのコメントを見ました。

バイナリ加算の詳細は ここ です。

4
Andrew Rollings

最初の数を2番目の数と同じくらい頻繁に増やしてみませんか?

2

ADDがビット単位の演算の組み合わせとしてではなく、単一の命令としてアセンブラに実装される理由は、実行が難しいためです。特定の下位ビットから次に上位のビットへのキャリーについて心配する必要があります。これは、マシンがハードウェアで高速に実行することですが、Cを使用しても、ソフトウェアで高速に実行することはできません。

2

pythonビット演算子の使用:

def sum_no_arithmetic_operators(x,y):
    while True:
        carry = x & y
        x = x ^ y
        y = carry << 1
        if y == 0:
            break
    return x
1
guribe94

2つの整数を追加することはそれほど難しくありません。オンラインでのバイナリ加算の例はたくさんあります。

さらに難しい問題は浮動小数点数です! http://pages.cs.wisc.edu/~smoler/x86text/lect.notes/arith.flpt.html に例があります。

1
Colin

コーディングインタビューでこれを問題18.1と見なしました。私のpython解決策:

def foo(a, b):
"""iterate through a and b, count iteration via a list, check len"""
    x = []
    for i in range(a):
            x.append(a)
    for i in range(b):
            x.append(b)
    print len(x)

この方法は反復を使用するため、時間計算量は最適ではありません。最善の方法は、ビット単位の演算を使用して下位レベルで作業することだと思います。

0
James

これは、ポータブルな1行の3項および再帰ソリューションです。

int add(int x, int y) {
    return y == 0 ? x : add(x ^ y, (x & y) << 1);
}
0
Eddie B

紙にバイナリ加算を行うのと同じ方法で実装されます。

int add(int x, int y)
{
    int t1_set, t2_set;
    int carry = 0;
    int result = 0;
    int mask = 0x1;

    while (mask != 0) {
        t1_set = x & mask;
        t2_set = y & mask;
        if (carry) {
           if (!t1_set && !t2_set) {
               carry = 0;
               result |= mask;
           } else if (t1_set && t2_set) {
               result |= mask;
           }
        } else {
           if ((t1_set && !t2_set) || (!t1_set && t2_set)) {
                result |= mask;
           } else if (t1_set && t2_set) {
                carry = 1;
           }
        }
        mask <<= 1;
    }
    return (result);
}

速度の改善は以下のようになります::

int add_better (int x, int y)
{
  int b1_set, b2_set;
  int mask = 0x1;
  int result = 0;
  int carry = 0;

  while (mask != 0) {
      b1_set = x & mask ? 1 : 0;
      b2_set = y & mask ? 1 : 0;
      if ( (b1_set ^ b2_set) ^ carry)
          result |= mask;
      carry = (b1_set &  b2_set) | (b1_set & carry) | (b2_set & carry);
      mask <<= 1;
  }
  return (result);
}
0
user3922199

C#で自分でこの問題に取り組んでいて、すべてのテストケースに合格することができませんでした。それから私は出くわしました this

C#6での実装は次のとおりです。

public int Sum(int a, int b) => b != 0 ? Sum(a ^ b, (a & b) << 1) : a;
0
Jake Smith

入力の符号が反対の場合、最も投票された回答は機能しません。ただし、次のようになります。私はある場所で不正行為をしましたが、コードを少しきれいに保つためだけです。改善のための提案は大歓迎です

def add(x, y):
if (x >= 0 and y >= 0) or (x < 0 and y < 0):
    return _add(x, y)
else:
    return __add(x, y)


def _add(x, y):
if y == 0:
    return x
else:
    return _add((x ^ y), ((x & y) << 1))


def __add(x, y):
if x < 0 < y:
    x = _add(~x, 1)
    if x > y:
        diff = -sub(x, y)
    else:
        diff = sub(y, x)
    return diff
Elif y < 0 < x:
    y = _add(~y, 1)
    if y > x:
        diff = -sub(y, x)
    else:
        diff = sub(y, x)
    return diff
else:
    raise ValueError("Invalid Input")


def sub(x, y):
if y > x:
    raise ValueError('y must be less than x')
while y > 0:
    b = ~x & y
    x ^= y
    y = b << 1
return x
0
lalatnayak

これはPythonでの私の実装です。バイト(またはビット)の数がわかっている場合は、うまく機能します。

def summ(a, b):
    #for 4 bytes(or 4*8 bits)
    max_num = 0xFFFFFFFF
    while a != 0:
        a, b = ((a & b) << 1),  (a ^ b)
        if a > max_num:
            b = (b&max_num) 
            break
    return b
0

ビットシフトとAND演算を使用してそれを行うことができます。

#include <stdio.h>

int main()
{
    unsigned int x = 3, y = 1, sum, carry;
    sum = x ^ y; // Ex - OR x and y
    carry = x & y; // AND x and y
    while (carry != 0) {
        carry = carry << 1; // left shift the carry
        x = sum; // initialize x as sum
        y = carry; // initialize y as carry
        sum = x ^ y; // sum is calculated
        carry = x & y; /* carry is calculated, the loop condition is
                        evaluated and the process is repeated until
                        carry is equal to 0.
                        */
    }
    printf("%d\n", sum); // the program will print 4
    return 0;
}
0
kyle k