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

时间:2013-09-10 13:41:27

标签: c++ algorithm bit-manipulation

我需要添加2个无符号数字'a'和'b'。

我使用位操作

找到了以下代码
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) );
}

我无法弄清楚这段代码是如何添加两个数字的。

任何帮助/指导人。

5 个答案:

答案 0 :(得分:6)

逻辑:代码实现了一系列half-adders,并通过递归将进位从其中一个传播到下一个。有关其工作原理的示例,请参阅“干运行”。

考虑这两个值a=0011b=0101。在基数10中,它们分别是a=3b=5

现在,a^b=01101仅当一位为1时)a&b=00011仅当两位均为1时,单个案例在哪里可以携带)。

然后,您需要将进位移动到下一位,这就是<<1操作的原因,即carry=0010

现在您需要使用上述算法添加01100010。这将变为添加01000100。这将导致00001000一起添加,这会导致1000添加0000,这将通过基本情况(b == 0)结束。

以表格形式:

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

最后一行是基本情况。

答案 1 :(得分:3)

牢记这一点:

2003 Standard C ++ 5.3.1c7:

  

无符号数量的负数是通过减去它来计算的   值来自2 ^ n,其中n是提升操作数中的位数。

a+b = a - (-b)将在符合2003标准(顺便提一下C ++ 11)的C ++编译器中工作。

当然,我会回答一个符合早期标准的答案(C ++ 99,例如-unsigned未定义)。

答案 2 :(得分:3)

问题中的位操作代码使用两个基本原则:half adder以及添加是可交换的事实。

单个半加法器增加两位,没有进位。如果输入中的一个输入为1,则单个位添加结果为1,如果输入相等则为0。这由代码中的按位xor表示。

即使这样做,你也需要处理这些行为。如果两个位都是1,则从位位置执行,否则为0。这是由按位and和后续shift的组合表示的,用于将进位移动到需要添加的位位置。

对add的递归调用使用添加是可交换的事实来应用进位。无论是随着初始添加逐位添加进位,还是在后续步骤中进行批量添加都无关紧要。

添加进位可能会导致新的结转。这是通过继续递归调用来处理的,直到添加没有进位。

必须达到递归基本情况,零进位,因为加零,零进位,不能导致进位。如果进位的最低有效k位在一个进位加法上都为零,则下一个进位的至少k+1个最低有效位必须为零。

答案 3 :(得分:1)

要理解函数实际上为什么添加两个数字,有必要查看真值表以增加两位:

a = 0,b = 0 - &gt; a + b = 00
a = 0,b = 1 - &gt; a + b = 01
a = 1,b = 0 - &gt; a + b = 01
a = 1,b = 1 - &gt; a + b = 10

您看到较低位是两个输入位的XOR,较高位是两个输入位的AND,因此最终结果由(a XOR b)OR((a AND B)&lt; &lt; 1)。由于此函数添加了32位数字,因此不能简单地对结果进行OR运算,因为在组合XOR和AND运算的结果时,一些额外的进位可以出现在高位数中,这就是为什么必须递归应用该函数的原因。 / p>

顺便说一句,这就是在硬件中添加数字的方式。

答案 4 :(得分:0)

这是硬件实现添加的方式。结果 一个位上的加法是独占或(^运算符 在C ++中)的位;这就是sum所得到的。但是这个 不考虑低位的任何进位。 执行是和(&运算符)的位和 为您提供carry的初始值。但是进行了 位n是位n + 1的进位,所以我们向左移位,移位 n进入位n + 1,并将其添加到。

我们使用递归来添加它,因为如果结果(在 位加入之前的进位是1,进位是1, 还会有一个执行。

为什么递归结束会更加微妙(当然, 硬件版本不会递归,而是增加额外的 逻辑)。考虑到这一点,最容易评估 原始值:

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

(“sum”列是a ^ b的结果,没有进位。)

在第一次递归调用时,b的第0位将为0(因为它 表示低阶位的carry_in,而没有 一个或因为<<,它移动了carry_out 位n到位n + 1的carry_in)。当然,还有 位0将为0.因此,对于第一次递归调用中的位0,仅 前两行可以发生。这意味着会有 没有carry_out,并且对于下一次递归,只有前两个 行与0和1相关。换句话说,每个 递归有效地从传播中消除了一个位集 携带,结果传播的携带最终必须 变为0.并且因为它作为参数b传播,参数 b最终必须变为0。