繁体   English   中英

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

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

我需要添加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) );
}

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

任何帮助/指导的人。

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

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

现在, a^b=01101仅当单个位为1 ),而a&b=00011仅当两个位为一,则单一的情况下可以有一个进位)。

然后,你需要将进位移动到下一位,这就是为什么你有<<1操作,使carry=0010

现在需要使用上述算法添加01100010 这将变成添加01000100 这将导致添加00001000 ,这将导致添加10000000 ,其将通过基本情况( 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 | ---- | ---- | ---- |

最后一行是基本情况。

记住这一点:

2003 Standard C ++ 5.3.1c7:

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

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

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

问题中的位操作代码使用两个基本原则: 半加法器和加法是可交换的这一事实。

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

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

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

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

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

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

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

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

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

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

暂无
暂无

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

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