使用32位算术添加64位数

时间:2009-10-30 22:32:58

标签: 64-bit 32-bit

我们如何使用32位算术添加两个64位数?

6 个答案:

答案 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中。