我需要添加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) );
}
我无法弄清楚这段代码是如何添加两个数字的。
任何帮助/指导人。
答案 0 :(得分:6)
逻辑:代码实现了一系列half-adders,并通过递归将进位从其中一个传播到下一个。有关其工作原理的示例,请参阅“干运行”。
考虑这两个值a=0011
和b=0101
。在基数10中,它们分别是a=3
和b=5
。
现在,a^b=0110
(1
仅当一位为1
时)a&b=0001
(1
仅当两位均为1时,单个案例在哪里可以携带)。
然后,您需要将进位移动到下一位,这就是<<1
操作的原因,即carry=0010
。
现在您需要使用上述算法添加0110
和0010
。这将变为添加0100
和0100
。这将导致0000
与1000
一起添加,这会导致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。