简体   繁体   English

添加无符号数字而不使用'+'或'++'

[英]Add unsigned numbers without using '+' or '++'

I need to add 2 unsigned numbers 'a' and 'b' . 我需要添加2个无符号数字'a'和'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 . 考虑这两个值a=0011b=0101 In base 10 they are a=3 and b=5 respectively. 在基数10中,它们分别是a=3b=5

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). 现在, a^b=01101仅当单个位为1 ),而a&b=00011仅当两个位为一,则单一的情况下可以有一个进位)。

Then, you need to move the carry to the next bit, that's why you have the <<1 operation, making carry=0010 . 然后,你需要将进位移动到下一位,这就是为什么你有<<1操作,使carry=0010

Now you need to add 0110 and 0010 using the above algorithm. 现在需要使用上述算法添加01100010 This will turn into adding 0100 and 0100 . 这将变成添加01000100 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 ). 这将导致添加00001000 ,这将导致添加10000000 ,其将通过基本情况( 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: 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. 通过从2 ^ n减去其值来计算无符号数量的负数,其中n是提升的操作数中的位数。

a+b = a - (-b) will work in a C++ compiler conforming to 2003 standard (and C++11 by the way). a+b = a - (-b)将在符合2003标准(顺便提一下C ++ 11)的C ++编译器中工作。

Of course, I'll upvote an answer that's conformant with earlier standards (C++99 for example where -unsigned is undefined). 当然,我会提出一个符合早期标准的答案(C ++ 99,例如-unsigned未定义)。

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. 如果输入中的一个输入为1,则单个位添加结果为1,如果输入相等则为0。 That is represented by the bitwise xor in the code. 这由代码中的按位xor表示。

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. 如果两个位都是1,则从位位置执行,否则为0。 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. 这通过按位and后续shift的组合来表示,以将进位移动到需要添加的位位置。

The recursive call to add applies the carries, using the fact that addition is commutative. 对add的递归调用使用添加是可交换的事实来应用进位。 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. 如果进位的最低有效k位在一次进位加法上全为零,则下一进位的至少k+1最低有效位必须为零。

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 = 0 - > a + b = 00
a = 0, b = 1 -> a + b = 01 a = 0,b = 1 - > a + b = 01
a = 1, b = 0 -> a + b = 01 a = 1,b = 0 - > a + b = 01
a = 1, b = 1 -> a + b = 10 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). 您会看到较低位是两个输入位的XOR,较高位是两个输入位的AND,因此最终结果由(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. 由于此函数添加了32位数字,因此不能简单地对结果进行OR运算,因为在组合XOR和AND运算的结果时,一些额外的进位可以出现在高位数中,这就是为什么必须递归应用该函数的原因。

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; 在位上添加的结果是位的排除或(C ++中的^运算符); this is what you get with sum . 这就是你得到的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 . 进位输出是比特(的和&运营商),它给你的初始值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. 但是位n的进位是位n + 1的进位,所以我们向左移动,将位n移动到位n + 1,并将其加入。

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. 我们使用递归来添加它,因为如果在添加进位之前的结果(在位级别)是1,并且进位是1,则还将有执行。

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.) (“sum”列是a ^ b的结果,没有进位。)

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). 在第一次递归调用时,b的第0位将为0(因为它表示低阶位的carry_in,并且没有一个 - 或者因为<< ,它将位n的进位输出移位到位的carry_in 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. 当然,位0的carry_in将为0.因此,对于第一次递归调用中的位0,只能出现前两行。 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. 这意味着不存在carry_out,并且对于下一次递归,只有前两行与位0和1相关。换句话说,每次递归都有效地从传播的进位中消除了一个位集,结果是传播了进位必须最终变为0.并且由于它作为参数b传播,参数b最终必须变为0。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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