使用 32 位算术添加 64 位数字

发布于 2024-08-10 09:06:45 字数 33 浏览 2 评论 0原文

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

How do we add two 64 bit numbers using 32 bit arithmetic??

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

向地狱狂奔 2024-08-17 09:06:45

首先添加最低有效字节,保留进位。考虑到 LSB 的进位,添加最高有效字节:

; x86 assembly, Intel syntax
; adds ecx:ebx to edx:eax
add eax, ebx
adc edx, ecx

Add the least significant bytes first, keep the carry. Add the most significant bytes considering the carry from LSBs:

; x86 assembly, Intel syntax
; adds ecx:ebx to edx:eax
add eax, ebx
adc edx, ecx
紫南 2024-08-17 09:06:45

考虑如何使用一位数算术将两个两位数相加。

 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”时,我的意思是“a 的 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。在基数 2^32 中,我们将 2^32 写为 10。如果在基数 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

现在,以 2^32 为基数将它们相加,就像以 10 为基数相加一样,只是,而不是使用数字算术相加使用 32 位算术进行加法!

如何将一个 64 位数字 a 拆分为两个 32 位数字 a_1 和 a_0?将 a 除以 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!

所有这些都假设您正在执行无符号算术,但从这里处理符号很容易。

Consider how you add two 2-digit numbers using 1-digit arithmetic.

 42
+39
---

First you add the right column. (The "ones" or "units"). 2+9 is 11. 11 "overflowed" your 1 digit arithmetic, so you have to "carry" the 10.

 1
 42
+39
---
  1

Now you add up the left "tens" column, including the carry. 1+4+3=8.

 1
 42
+39
---
 81

8 is less than 10, so no carry. You're done.

What just happened? When you say that a number is "42" (in base 10) you really mean

4*10+2

Or, equivalently,

4*10^1+2*10^0

(when I say "a^b", like "10^1", I mean "a raised to the b'th power": a multiplied by itself b times. 10^0 is 1. 10^1 is 10. 10^2 is 10*10=100...)

When you add "42" and "39" you are saying

4*10+2+3*10+9

Which equals

(4+3)*10+(2+9)*1
(4+3)*10+(11)*1
(4+3)*10+(1*10+1)*1

Now since "11" isn't a valid one digit number, you need to carry 10 from the ones, turning it into a 1 in the tens place.

(4+3)*10+(1)*10+(1)*1
(4+3+1)*10+(1)*1
(8)*10+(1)*1

which is 81.

So, why have I been talking about this rather than your question about 64 bit numbers and 32 bit arithmetic? Because they are actually exactly the same!

A digit ranges from 0 to 9. A "32 bit number" ranges from 0 to 2^32-1. (Assuming it is unsigned.)

So, rather than working in base 10, let's work in base 2^32. In base 2^32, we write 2^32 as 10. If you write a 64 bit number in base 2^32, it would be

(x)*10+y

where x and y are symbols for numbers between 0 and 2^32-1. Those symbols are bitstrings.

If you want to add two 64 bit numbers, break them down in base 2^32 as:

 a_1*10+a_0
+b_1*10+b_0

Now you add them in base 2^32 the exact same way you add them in base 10 -- just, rather than adding using digit arithmetic you add using 32 bit arithmetic!

How do you split a 64 bit number a into two 32 bit numbers a_1 and a_0? Divide a by 2^32. Not in floating point, but integerwise -- where you get a dividend and a remainder. The dividend is a_1, the remainder is a_0.

Unfortunately that requires 64 bit arithmetic. However, typically a_1 is the "most significant half" of a, so, in C style syntax:

a_1=(a >> 32)
a_0=(a & 0xFFFFFFFF)

where >> is a right bitshift and & is bitwise and.

Typically if you can't do 64 bit addition, a "64 bit number" will actually be the two 32 bit numbers a_1 and a_0. You won't have a uint64_t if you can't do uint64_t arithmetic!

All this assumes that you're doing unsigned arithmetic, but dealing with signs is easy from here.

╰◇生如夏花灿烂 2024-08-17 09:06:45

之前发布的 C 代码不必要地冗长:

unsigned a1, b1, a2, b2, c1, c2;

c1 = a1 + b1;
c2 = a2 + b2;

if (c1 < a1)
    c2++;

“if”中的“a1”也可以替换为“b1”。
溢出时,c1 将小于 a1 和 b1。

The C code previously posted is unnecessarily verbose:

unsigned a1, b1, a2, b2, c1, c2;

c1 = a1 + b1;
c2 = a2 + b2;

if (c1 < a1)
    c2++;

The 'a1' in the 'if' can be replaced with 'b1' as well.
On overflow, c1 will be less than both a1 and b1.

不喜欢何必死缠烂打 2024-08-17 09:06:45

如果 64 位数字是 (a2,a1) 和 (b2,b1) ,其中 x2 是取为无符号的高 32 位,x1 是取为无符号的低 32 位作为无符号,则两个数字的总和如下所示。

c1 = a1 + b1

c2 = a2 + b2

if (c1 < a1 || c1 < b1)
   c2 += 1

If the 64-bit numbers are (a2,a1) and (b2,b1), where x2 is the high 32 bits taken as unsigned, and x1 is the low 32 bits taken as unsigned, then the sum of the two numbers is given below.

c1 = a1 + b1

c2 = a2 + b2

if (c1 < a1 || c1 < b1)
   c2 += 1
行至春深 2024-08-17 09:06:45

看起来像这样

/* 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 位操作数。

it looks something like this

/* 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;
}

Actual hardware doesn't use 32 bits to add 16 bits at a time, only one extra bit of carry is ever needed for addition, and almost all CPU's have a carry status flag for when an addition operation overflowed, so if you are using a 32 bit cpu, you can add 64 bit operands in two, 32 bit operations.

揽清风入怀 2024-08-17 09:06:45

几乎每个处理器都有进位位和进位相加操作,只有在进行汇编编程时您才会关心这些操作。如果您使用的是高级语言,编译器会转储完全相同的加进位操作。我的 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 中。

Pretty much every processor has the carry bit and add-with-carry operation, which you only care about if you're programming in assembly. If you're using a higher language, the compiler dumps out the exact same add-with-carry operations. My AVR-GCC gave me this when adding two 16-bit numbers--the AVR is 8-bit--but the same concepts would apply for higher processors.

Given the numbers are in registers R1:R2 and 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

The result is stored into R1:R2.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文