简体   繁体   中英

Add unsigned numbers without using '+' or '++'

I need to add 2 unsigned numbers 'a' and 'b' .

I found the following Code , using bit operations

unsigned int add (unsigned int a,unsigned int b)
{
    unsigned int carry, sum;
    if (b == 0)
    {
        return a;
    }

    sum = a ^ b; // xor takes sum
    carry = a & b; // collect carry;
    carry = carry << 1;
    return ( add (sum, carry) );
}

I cant figure out how is this code adding two numbers .

Any help/direction people .

The logic : The code implements a series of half-adders and propagates the carry from one of them to the next one via recursion. See the dry-run for an example on how this works.

Consider these two values a=0011 and b=0101 . In base 10 they are a=3 and b=5 respectively.

Now, a^b=0110 ( 1 only when a single bit is 1 ) while a&b=0001 ( 1 only when both bits is one, the single case where you can have a carry).

Then, you need to move the carry to the next bit, that's why you have the <<1 operation, making carry=0010 .

Now you need to add 0110 and 0010 using the above algorithm. This will turn into adding 0100 and 0100 . Which will result in adding 0000 with 1000 which will result in adding 1000 with 0000 which will end via the base case ( b == 0 ).

In tabular form:

|   a  |   b  | a^b  |  a&b | carry|
------------------------------------
| 0011 | 0101 | 0110 | 0001 | 0010 |
| 0110 | 0010 | 0100 | 0010 | 0100 |
| 0100 | 0100 | 0000 | 0100 | 1000 |
| 0000 | 1000 | 1000 | 0000 | 0000 |
| 1000 | 0000 | ---- | ---- | ---- |

Last row is base case.

Bear this in mind:

The 2003 Standard C++ 5.3.1c7:

The negative of an unsigned quantity is computed by subtracting its value from 2^n, where n is the number of bits in the promoted operand.

a+b = a - (-b) will work in a C++ compiler conforming to 2003 standard (and C++11 by the way).

Of course, I'll upvote an answer that's conformant with earlier standards (C++99 for example where -unsigned is undefined).

The bit manipulation code in the question uses two basic principles: half adder and the fact that addition is commutative.

A single half adder adds two bits, with no carry-in. The single bit add result is one if exactly one of the inputs is one, zero if the inputs are equal. That is represented by the bitwise xor in the code.

Even after doing that, you need to deal with the carries. The carry out from a bit position is one if both bits are one, zero otherwise. That is represented by the combination of a bitwise and , with a following shift to move the carry to the bit position where it needs to be added.

The recursive call to add applies the carries, using the fact that addition is commutative. It does not matter whether the carries are added bit-by-bit along with the initial addition, or in bulk in a later step.

Adding in a carry may cause a new carry-out. That is handled by continuing the recursive calls until an add has no carries.

The recursion base case, zero carry, must be reached because adding zero, with zero carry-in, cannot result in a carry. If the least significant k bits of the carry are all zero on one carry addition, at least k+1 least significant bits of the next carry must be zero.

To understand why the function does in fact add two numbers it is helpful to look at the truth table for addition of two bits:

a = 0, b = 0 -> a + b = 00
a = 0, b = 1 -> a + b = 01
a = 1, b = 0 -> a + b = 01
a = 1, b = 1 -> a + b = 10

You see that the lower bit is the XOR of both input bits and the higher bit is the AND of the two input bits, so the final result is represented by (a XOR b) OR ((a AND B) << 1). As this function adds 32-bit numbers you cannot simply OR the results anymore because some additional carry bits can appear in the higher digits when combining the results of the XOR and the AND operations, and that's why you have to apply the function recursively.

Btw, that is pretty much the way addition of numbers is done in hardware.

This is the way the hardware implements addition. The results of an addition on a bit are the exclusive or (the ^ operator in C++) of the bits; this is what you get with sum . But this doesn't take into consideration any carry from the lower bit. The carry out is the and of the bits (the & operator), which gives you the initial value of carry . But the carry out of bit n is the carry in of bit n + 1, so we shift left, moving bit n into bit n + 1, and add it in.

We use recursion to add it in, because if the results (at the bit level) before adding carry in are 1, and carry in is 1, there will also be a carry out.

It's a bit more subtle why the recursion ends (and of course, the hardware version doesn't recurse, but rather adds additional logic). This is most easily evaluated by considering the original values:

a   b   carry_in  sum carry_out

0   0       0      0      0
1   0       0      1      0
0   1       0      1      0
1   1       0      0      1
0   0       1      0      0
1   0       1      1      1
0   1       1      1      1
1   1       1      0      1

(The "sum" column is the result of a ^ b , without the carry.)

On the first recursive call, bit 0 of b will be 0 (because it represents the carry_in of the lower order bit, and there isn't one—or because of the << , which moves the carry_out of bit n to carry_in of bit n + 1). And of course, the carry_in of bit 0 will be 0. So for bit 0 in the first recursive call, only the first two lines can occur. Which means that there will be no carry_out, and for the next recursion, only the first two lines are relevant for bits 0 and 1. In other words, each recursion effectively eliminates one set bit from the propagated carry, with the result that the propagated carry must eventually become 0. And since it is propagated as parameter b, parameter b must eventually become 0.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM