【问题标题】:How can i add two numbers with 12 bytes each-one?如何将两个数字相加,每个数字有 12 个字节?
【发布时间】:2019-06-23 23:33:47
【问题描述】:

我想将两个 12 字节的数字相加,并将结果存储在 16 字节的 var 中。我该怎么做?

section .data
    big_num1 dd 0x11111111, 0x22222222, 0x33333333
    big_num2 dd 0xffffffff, 0x22222222, 0x33333333
section .bss   
    result_4word resd 4

我想我可以将数字 1 的前 4 个字节数字与数字 2 的其他前 4 个字节相加,依此类推.. 但我不知道如何在我的结果变量中连接结果。 如果需要,我该怎么做? 这个解决方案合适吗?

xor eax, eax
xor ebx, ebx
mov ecx, 3
loop1:
mov eax, dword[big_num1+4*(ecx-1)]
mov ebx, dword[big_num2+4*(ecx-1)]
mov [result_4word+4*(ecx-1)], eax
adc [result_4word+4*(ecx-1)], ebx
loop loop1

【问题讨论】:

  • 阅读指令集参考中adc的条目。
  • 您无需验证任何内容。 adc 会为你做这件事。用add 添加最低的双字,其余的使用adc。完成。
  • 是的,但这是adc 的明确目的,因此您不必手动操作。
  • 它确实有效,但你只有最后添加的一个进位,你不能将它存储在任何地方。
  • 操作数和结果的大小仅受内存限制。如果您想将 100 万位数字添加到另一个 100 万位数字中,超级简单。第一个字节/字/双字无论您选择使用添加,其余使用 adc。

标签: assembly x86 bigint carryflag extended-precision


【解决方案1】:

小学:

   1234
+  5678
========

开始填写

     1
   1234
+  5678
========
      2

4+8 = 12,所以 2 携带 1。

在计算机中,您会 加一 = 4 + 8 ADC b = 3 + 7 ADC c = 2 + 6 adc d = 1 + 5

然后 dcba 包含您的结果,它可以根据需要缩放。 d,c,b,a 可以是 8 位、16 位、32 位或 64 位,具体取决于指令集。如果它们有标志,大多数都有 add 和 adc,那些没有标志的,那么你可以以各种不难的方式合成它们......(使用 32 位寄存器/内存将你的操作数分解为 16 位数量做 32现在位添加第 16 位是您的执行,将其添加到下一个 16 位块中,需要一些移位和掩码,但它们的工作原理都是一样的,因为您可能有 adc,那么您不需要做任何事情,只需做一些微不足道的添加, adc,adc, adc...直到完成。

如果在开始之前清除标志,则可以在循环中使用 adc。

现在,如果您的变量与处理器中的加法器不一致,那么您必须以某种方式对其进行综合。

同一个问题的小学数学,现在你必须分开做列。

  4
+ 8
====
 12

你必须手动屏蔽和移动结果 (12>>1) % 9 = 1 in base 10.

  1
  3
+ 7
====
 11

然后

  1
  2
+ 6
====
  9

这个是零

  0
  1
+ 5
====
  6

【讨论】:

    【解决方案2】:
    big_num1 dd 0x11111111, 0x22222222, 0x33333333
    big_num2 dd 0xffffffff, 0x22222222, 0x33333333
    

    这里定义了哪些数字?

    因为 是little-endian 架构,所以数字的最低部分存储在内存中的最低地址。对于 big_num1,第一个定义的 dword(值为 0x11111111)位于最低地址,因此是数字的最低部分。在正常的数字表示中,这就是右手边的内容。

    big_num1 == 0x333333332222222211111111
    big_num2 == 0x3333333322222222FFFFFFFF
    

    添加大数

    您从右到左添加相应的数字,就像每个人在学校学到的一样。

    在这些数字的十六进制表示中,需要考虑 24 位数字。但是由于架构是 32 位的,我们可以很好地制作 3 组 8 位数字。

    对于第一组,我们只需使用ADD

    mov     eax, [big_num1]           ;   0x11111111
    add     eax, [big_num2]           ; + 0xFFFFFFFF <-- This produces a carry
    mov     [result_4dword], eax      ;   0x00000000
    

    对于第 2 组,我们使用 ADC 来从之前的添加中选择一个可能的进位:

    mov     eax, [big_num1 + 4]       ;   0x22222222
    adc     eax, [big_num2 + 4]       ; + 0x22222222 + CF=1  <-- No new carry
    mov     [result_4dword + 4], eax  ;   0x44444445
    

    对于第 3 组,我们使用 ADC 来从之前的添加中选择一个可能的进位:

    mov     eax, [big_num1 + 8]       ;   0x33333333
    adc     eax, [big_num2 + 8]       ; + 0x33333333 + CF=0  <-- No new carry
    mov     [result_4dword + 8], eax  ;   0x66666666
    

    把它变成一个循环

    这里的关键是我们也可以将ADC 用于第一组如果我们事先明确清除进位标志

    clc
    mov     eax, [big_num1]           ;   0x11111111
    adc     eax, [big_num2]           ; + 0xFFFFFFFF + CF=0 <-- This produces a carry
    mov     [result_4dword], eax      ;   0x00000000
    

    现在我们可以编写一个包含 3 次迭代的循环,但我们必须小心不要无意中更改进位标志。这就是为什么我使用LEA 而不是ADD 来提高偏移量的原因。 DEC 也是不破坏进位标志的指令。我更喜欢组合 DEC ECX JNZ ... 因为它比 LOOP ... 更好:

        mov     ecx, 3
        xor     ebx, ebx              ; This additionally clears the carry flag
    Again:
        mov     eax, [big_num1 + ebx]
        adc     eax, [big_num2 + ebx] ; Can produce a new carry flag
        mov     [result_4dword + ebx], eax
        lea     ebx, [ebx + 4]        ; This does not clobber the carry flag
        dec     ecx                   ; This does not clobber the carry flag
        jnz     Again
    

    如果在这 3 个加法之后仍然有一个 set 进位,你必须在 result_4dword 的第 4 个 dword 中写一个 1,否则你必须写0 在这里。因为 result_4dword 在 .bss 部分,所以你不应该指望任何像零这样的预设值!

        setc    cl
        mov     [result_4dword + ebx], ecx  ; ECX=[0,1]
    

    请注意,我已将 result_4word 更改为 result_4dword。更有意义...

    【讨论】:

    • 是的,dec/jnz 是您在 Sandybridge 家族的最佳选择,在该家族中,部分旗帜摊位不是问题。在早期的英特尔上,比如 Nehalem 和 Core 2,您可能想要保存/恢复标志或可能使用 LEA / JECXZ / JMP 循环(但这有点糟糕,所以你需要展开以隐藏该成本)。 Problems with ADC/SBB and INC/DEC in tight loops on some CPUs。除了最近的 AMD 之外,loop 在所有方面都很慢,这真是太糟糕了,因为它非常适合 ADC 循环
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多