[我知道这真的很老了,但我有同样的问题,而且由于最重要的答案是矛盾的......]
一个人的恭维确实是分配性的。您的代码中的问题(以及 Kaganar 的那种但不正确的答案)是您正在丢弃进位位 - 您可以在两个 s 的恭维中做到这一点,但不能在一个人的恭维中做到这一点。
无论您使用什么来存储总和,都需要比求和更多的内存空间,这样您就不会丢失进位位。然后将进位位折叠回用于存储操作数的位数以获得正确的总和。这在一个人的补码算术中称为“结束进位”。
来自维基百科文章(https://en.wikipedia.org/wiki/Signed_number_representations#Ones '_complement):
要将这个系统中表示的两个数字相加,需要进行传统的二进制加法,但随后需要进行结束进位:即,将任何结果进位加回到结果和中。要了解为什么这是必要的,请考虑以下示例,该示例显示了将 -1 (11111110) 与 +2 (00000010) 相加的情况:
binary decimal
11111110 –1
+ 00000010 +2
─────────── ──
1 00000000 0 ← Not the correct answer
1 +1 ← Add carry
─────────── ──
00000001 1 ← Correct answer
在前面的例子中,第一个二进制加法得到 00000000,这是不正确的。正确的结果 (00000001) 仅在重新添加进位时出现。
我稍微更改了您的代码,以便更轻松地自己进行数学检查和测试。使用有符号整数数据类型可能需要更多考虑,或者考虑端接借用而不是携带。我没有走那么远,因为我的应用程序都是关于校验和的(即无符号的,只有加法)。
unsigned short n1 = 5; //using 16-bit unsigned integers
unsigned short n2 = 3;
unsigned long oc_added = (unsigned short)~n1 + (unsigned short)~n2; //32bit
/* Fold the carry bits into 16 bits */
while (oc_added >> 16)
oc_added = (oc_added & 0xffff) + (oc_added >> 16);
unsigned long oc_sum = ~(n1 + n2); //oc_sum has 32 bits (room for carry)
/* Fold the carry bits into 16 bits */
while (oc_sum >> 16)
oc_sum = (oc_sum & 0xffff) + (oc_sum >> 16);
int result = oc_added == oc_sum;
unsigned short calc = ~n1;
unsigned short calc0 = ~n2;
unsigned short calc1 = ~n1 + ~n2; //loses a carry bit
unsigned short calc2 = ~(n1 + n2);
printf("(Part B: n1 is %d, n2 is %d\n", n1, n2);
printf("Part B: (calc is: %d and calc0 is: %d\n", calc, calc0);
printf("Part B: (calc1 is: %d and calc2 is: %d\n", calc1, calc2);
printf("Part B: (~%d + ~%d) == ~(%d + %d) evaluates to %d\n", n1, n2, n1, n2, result);