我们如何使用32位算术添加两个64位数?
答案 0 :(得分:22)
首先添加最低有效字节,保持进位。考虑来自LSB的进位,添加最重要的字节:
; x86 assembly, Intel syntax
; adds ecx:ebx to edx:eax
add eax, ebx
adc edx, ecx
答案 1 :(得分:15)
考虑如何使用1位数运算添加两个2位数字。
42
+39
---
首先添加正确的列。 (“那些”或“单位”)。 2 + 9是11. 11“溢出”你的1位数算术,所以你必须“携带”10。
1
42
+39
---
1
现在你加上左边的“十”列,包括进位。 1 + 4 + 3 = 8。
1
42
+39
---
81
8小于10,所以没有携带。你已经完成了。
刚刚发生了什么?当你说一个数字是“42”(基数为10)时,你的意思是
4*10+2
或者,相当于
4*10^1+2*10^0
(当我说“a ^ b”,如“10 ^ 1”时,我的意思是“提升到b'次幂”:a乘以b次.10 ^ 0是1. 10 ^ 1是10. 10 ^ 2是10 * 10 = 100 ...)
当您添加“42”和“39”时,您正在说
4*10+2+3*10+9
等于
(4+3)*10+(2+9)*1
(4+3)*10+(11)*1
(4+3)*10+(1*10+1)*1
现在因为“11”不是有效的一位数字,你需要从那些数字中携带10,将它变成十位数的1。
(4+3)*10+(1)*10+(1)*1
(4+3+1)*10+(1)*1
(8)*10+(1)*1
这是81。
那么,为什么我一直在讨论这个而不是关于64位数和32位算术的问题呢?因为它们实际上完全一样!
数字范围为0到9.“32位数”的范围为0到2 ^ 32-1。 (假设它是未签名的。)
所以,不要在10号基地工作,让我们在2 ^ 32基地工作。在base 2 ^ 32中,我们将2 ^ 32写为10.如果你在base 2 ^ 32中写一个64位数字,它将是
(x)*10+y
其中x和y是0到2 ^ 32-1之间数字的符号。那些符号是位串。
如果要添加两个64位数字,请在基数2 ^ 32中将它们分解为:
a_1*10+a_0
+b_1*10+b_0
现在你在base 2 ^ 32中添加它们的方法与在base 10中添加它们完全相同 - 只是,而不是使用32位算术添加使用数字运算添加!
如何将64位数字a分成两个32位数a_1和a_0?除以2 ^ 32。不是浮点数,而是整数 - 你得到一个红利和余数。股息为a_1,余数为a_0。
不幸的是,这需要64位算术。但是,通常a_1是a的“最重要的一半”,因此,在C风格语法中:
a_1=(a >> 32)
a_0=(a & 0xFFFFFFFF)
其中>>是一个正确的位移和&是按位和。
通常,如果不能进行64位加法,则“64位数”实际上是两个32位数a_1和a_0。如果你不能做uint64_t算术,你将没有uint64_t!
所有这些都假设您正在进行无符号算术,但是从这里处理符号很容易。
答案 2 :(得分:7)
以前发布的C代码不必要地冗长:
unsigned a1, b1, a2, b2, c1, c2;
c1 = a1 + b1;
c2 = a2 + b2;
if (c1 < a1)
c2++;
'if'中的'a1'也可以替换为'b1'。 溢出时,c1将小于a1和b1。
答案 3 :(得分:6)
如果64位数是(a 2 , 1 )和(b 2 ,b 1 ),其中 x 2 是高32位作为无符号, x 1 是低32位作为无符号,然后下面给出两个数字的总和。
c1 = a1 + b1
c2 = a2 + b2
if (c1 < a1 || c1 < b1)
c2 += 1
答案 4 :(得分:3)
它看起来像这样
/* break up the 64bit number into smaller, 16bit chunks */
struct longint {
uint16 word0;
uint16 word1;
uint16 word2;
uint16 word3;
};
uint16 add(longint *result, longint *a, longint *b)
{
/* use an intermediate large enough to hold the result
of adding two 16 bit numbers together. */
uint32 partial;
/* add the chunks together, least significant bit first */
partial = a->word0 + b->word0;
/* extract thie low 16 bits of the sum and store it */
result->word0 = partial & 0x0000FFFF;
/* add the overflow to the next chunk of 16 */
partial = partial >> 16 + a->word1 + b->word1;
/* keep doing this for the remaining bits */
result->word1 = partial & 0x0000FFFF;
partial = partial >> 16 + a->word2 + b->word2;
result->word2 = partial & 0x0000FFFF;
partial = partial >> 16 + a->word3 + b->word3;
result->word3 = partial & 0x0000FFFF;
/* if the result overflowed, return nonzero */
return partial >> 16;
}
实际硬件不会一次使用32位来添加16位,只需要一个额外的进位来添加,并且几乎所有CPU都有一个进位状态标志,用于当加法运算溢出时,所以如果你正在使用32位cpu,你可以在两个32位操作中添加64位操作数。
答案 5 :(得分:1)
几乎每个处理器都有进位和附加进位操作,如果你在汇编中编程,你只关心它。如果您使用的是更高级的语言,编译器会转储完全相同的add-with-carry操作。我的AVR-GCC在添加两个16位数时给了我这个 - AVR是8位 - 但相同的概念适用于更高的处理器。
鉴于数字在寄存器R1:R2和R3:R4:
中ADD R2,R4 ; first add the two low-bytes, result stored into R2
ADC R1,R3 ; then the two high-bytes and the carry bit, into R1
结果存储在R1:R2中。