Math 在我的汇编程序中,我试图计算((((2^0+;2^1)*2^2)和#x2B;2^3)*2^4)和#x2B;2^5)的方程

Math 在我的汇编程序中,我试图计算((((2^0+;2^1)*2^2)和#x2B;2^3)*2^4)和#x2B;2^5)的方程,math,assembly,x86,Math,Assembly,X86,在我的80x86汇编程序中,我试图计算 (((((2^0+2^1)*2^2)+2^3)*2^4)+2^5)…(2^n),其中每个偶数指数前面有一个乘法,每个奇数指数前面有一个加号。我有代码,但我的结果与期望的结果不断偏离。当n取5时,结果应该是354,但我得到330 如有任何建议,将不胜感激 .586 .model flat include io.h .stack 4096 .data number dword ? prompt byte "enter the power", 0 stri

在我的80x86汇编程序中,我试图计算 (((((2^0+2^1)*2^2)+2^3)*2^4)+2^5)…(2^n),其中每个偶数指数前面有一个乘法,每个奇数指数前面有一个加号。我有代码,但我的结果与期望的结果不断偏离。当n取5时,结果应该是354,但我得到330

如有任何建议,将不胜感激

.586
.model flat

include io.h

.stack 4096

.data
number dword ?
prompt byte "enter the power", 0
string byte 40 dup (?), 0
result byte 11 dup (?), 0
lbl_msg byte "answer", 0
bool dword ?
runtot dword ?

.code
_MainProc proc
    input prompt, string, 40
    atod string
    push eax


    call power



    add esp, 4

    dtoa result, eax
    output lbl_msg, result

    mov eax, 0
    ret

_MainProc endp

power proc
    push ebp
    mov ebp, esp

    push ecx

    mov bool, 1     ;initial boolean value
    mov eax, 1
    mov runtot, 2   ;to keep a running total
    mov ecx, [ebp + 8]

    jecxz done

loop1:
    add eax, eax        ;power of 2
    test bool, ecx      ;test case for whether exp is odd/even
    jnz oddexp          ;if boolean is 1
    add runtot, eax     ;if boolean is 0
    loop loop1

oddexp:
    mov ebx, eax        ;move eax to seperate register for multiplication
    mov eax, runtot     ;move existing total for multiplication
    mul ebx             ;multiplication of old eax to new eax/running total
    loop loop1

done:
    mov eax, runtot     ;move final runtotal for print
    pop ecx
    pop ebp
    ret




power endp



end

使用静态变量和分支将代码过度复杂化

这些是2的幂,您可以(也应该)左移
n
,而不是实际构造
2^n
并使用
mul
指令

添加eax,eax
是乘以2(即左移位乘以1)的最佳方法,但不清楚为什么要在此时对eax中的值执行此操作。它要么是乘法结果(您可能应该在
mul
之后将其存储回
runtot
),要么是在偶数迭代后左移1

如果您试图创建一个
2^i
变量(强度降低优化为每次迭代移位1,而不是移位
i
),那么您的错误在于在
oddexp
块中使用
mul
及其设置来敲击EAX

正如Jester指出的,如果第一个
循环loop1
失败,它将进入
oddexp:
。当你做循环尾部复制时,请确保如果循环结束的话,你会考虑从每个尾部从何处掉下来。


使用名为
bool
的静态变量也没有意义,该变量保存
1
,您只能将其用作
test
的操作数。这对人类读者意味着面具有时需要改变<代码>测试ecx,1作为检查低位是否为零/非零的一种方法更清晰

对于
runtot
,您也不需要静态存储,只需使用一个寄存器(比如EAX,您希望最终得到结果)。32位x86有7个寄存器(不包括堆栈指针)


我就是这样做的。未经测试,但通过2展开,我简化了很多。然后奇数/偶数的测试消失了,因为交替模式被硬编码到循环结构中

我们在循环中增加和比较/分支两次,因此展开并没有消除循环开销,只是将一个循环分支更改为一个可以从中间离开循环的an
if()break

这不是写这篇文章最有效的方式;循环中的增量和早期退出检查可以通过从<代码> N< /代码>中计数另一个计数器来优化,如果左下2个步骤,则离开循环。(然后在结语中整理)

前几项产出是:

n          shiftadd(n) (base2)

0                   1
1                  11
2                1100
3               10100     ; 1100 + 1000 carries
4           101000000
5           101100000     ; 101000000 + 100000 set a bit that was previously 0
6     101100000000000
7     101100010000000     ; increasing amounts of trailing zero around the bit being flipped by ADD
剥离前3次迭代将启用BTS优化,您只需设置位,而不是实际创建
2^n
并添加


我们可以对大于n的
i=3
的起始点进行硬编码,并优化代码,计算出
n=18的返回值,最后的移位计数严格大于寄存器宽度的一半,并且奇数
i
中的2^i没有低位。因此,只有最后1或2次迭代才能影响结果。它归结为
1您的代码分为
oddexp
。您需要在
oddexp:
之前完成
jmp。你可能还有其他问题。学习使用调试器。一旦ECX为0,代码将自动跳转到“完成”。调试器已经被彻底使用了。使用我提供的代码,当n=1时,结果是2。结果应该是3,当运行代码时,结果是330。我觉得某个地方有一个因子,但我不知道应该在哪里添加或调整它。我不确定我是否正确地遵循了您的预期算法,但正如我所说的,如果您想保持两个的幂,我会确保
eax
被保留,然后将乘法的结果写回
runtot
。这是2的幂,你可以(也应该)左移1。(或者,对于乘以2^n,左移一个
cl
中的计数,因此朝
n
方向向上计数
cl
)。我也不认为有一个名为
bool
的静态变量有什么意义,您似乎只将它用作
test
的操作数。与编写
testecx,1
检查低位是否为零/非零相比,您只是使代码更复杂、更难阅读。对于
runtot
,您也不需要静态存储,只需使用寄存器即可。32位x86有7个寄存器(不包括堆栈指针)。
; define shiftadd_power(n) { local res=1; local i; for(i=1;i<=n;i++){ res+=1<<i; i++; if(i>n)break; res<<=i;} return res;}
shiftadd_power(n) defined
; base2(2)

; shiftadd_power(0)
        1 /* 1 */
...
n          shiftadd(n) (base2)

0                   1
1                  11
2                1100
3               10100     ; 1100 + 1000 carries
4           101000000
5           101100000     ; 101000000 + 100000 set a bit that was previously 0
6     101100000000000
7     101100010000000     ; increasing amounts of trailing zero around the bit being flipped by ADD
;; UNTESTED
;; special cases for n<3, and for n>=18
;; enabling an optimization in the main loop (BTS instead of add)
;; funky overflow behaviour for n>31: large odd n gives 1<<(n%32) instead of 0
power_optimized proc
     ; fastcall calling convention: arg: ECX = unsigned int n <= 31
     ; clobbers: ECX, EDX
     ; returns: EAX

    mov   eax, 14h      ; 0b10100 = power(3)
    cmp   ecx, 3
    ja    n_gt_3        ; goto main loop or fall through to hard-coded low n
    je    early_ret
    ;; n=0, 1, or 2  =>  1, 3, 12  (0b1, 0b11, 0b1100)

    mov   eax, 0ch      ; 0b1100  to be right-shifted by 3, 2, or 0
    cmp   ecx, 1        ; count=0,1,2 => CF,ZF,neither flag set
    setbe cl            ; count=0,1,2 => cl=1,1,0
    adc   cl, cl        ;                   3,2,0  (cl = cl+cl + (count<1) )
    shr   eax, cl
early_ret:
    ret

large_n:                ; odd n: result = 1<<n.  even n: result = 0
    mov   eax, ecx
    and   eax, 1        ; n&1
    shl   eax, cl       ; n>31 will wrap the shift count so this "fails"
    ret                 ; if you need to return 0 for all n>31, add another check

n_gt_3:
    ;; eax = running total for i=3 already
    cmp   ecx, 18
    jae   large_n

    mov   edx, ecx      ; EDX = n
    mov   ecx, 4        ; ECX = i=4..n loop counter and shift count

loop1:                  ; do{   // unrolled by 2
    ; multiply by 2^even power
    shl   eax, cl       ; total <<= i;  // same as total *= (1<<i)

    inc   edx
    cmp   ecx, edx
    jae   done            ; if (++i >= n) break;

    ; add 2^odd power.  i>3 so it won't already be set (thus no carry)
    bts   eax, edx      ; total |= 1<<i;

    inc   ecx           ; ++i
    cmp   ecx, edx
    jb    loop1         ; }while(i<n);

done:
    ret