最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

5.8 匯編語言:匯編高效除法運(yùn)算

2023-08-23 09:55 作者:bili_42682284418  | 我要投稿

通常情況下計算除法會使用`div/idiv`這兩條指令,該指令分別用于計算無符號和有符號除法運(yùn)算,但除法運(yùn)算所需要耗費(fèi)的時間非常多,大概需要比乘法運(yùn)算多消耗10倍的CPU時鐘,在Debug模式下,除法運(yùn)算不會被優(yōu)化,但Release模式下,除法運(yùn)算指令會被特定的算法經(jīng)過優(yōu)化后轉(zhuǎn)化為為乘法,這樣就可以提高除法運(yùn)算的效率。


?- 1.如果被除數(shù)是一個未知數(shù),那么編譯器無法確定數(shù)值,則編譯器會使用原始的`div`指令計算,程序的執(zhí)行效率會變低。

?- 2.如果除數(shù)是2的次冪,那么可以將其轉(zhuǎn)化為處理速度快的`shr`邏輯右移指令指令,該指令的執(zhí)行只需要1個時鐘周期,效率最高。

?- 3.如果要進(jìn)行2的次冪,并且該數(shù)是有符號數(shù),則只需要使用`sar`算數(shù)右移指令,即可進(jìn)行快速除法運(yùn)算。


### 8.1 使用IDIV指令完成除法


與乘法運(yùn)算相同,在不考慮效率前提下,完全可以使用`IDIV`指令完成除法運(yùn)算,該指令比乘法還慢。


?- 計算除法時應(yīng)遵循:

? - 如果除數(shù)為`8位`被除數(shù)為`16位`,則結(jié)果的商存放在`AL`中,余數(shù)存放`AH`中

? - 如果除數(shù)為`16位`被除數(shù)為`32位`,則結(jié)果的商存放與`AX`中,余數(shù)存放`DX`中

? - 如果除數(shù)為`32位`被除數(shù)為`64位`,則結(jié)果的商存放與`EAX`中,余數(shù)存放`EDX`中

? - 指令`CDQ`用于擴(kuò)展寄存器,擴(kuò)展后`EDX`存儲高位而`EAX`存儲低位


除法指令計算很簡單,只需要擴(kuò)展CDQ寄存器,然后累計除即可。

```ASM

.data

? ? x DWORD ?

? ? y DWORD ?

? ? szFmt BYTE '計算結(jié)果: %d',0dh,0ah,0

.code

? ? main PROC

? ? ? mov dword ptr ds:[x],1000

? ? ? mov dword ptr ds:[y],20

? ? ??

? ? ? ; 計算 x / y

? ? ? mov eax,dword ptr ds:[x]? ?; eax = 1000

? ? ? cdq? ? ? ? ? ? ? ? ? ? ? ? ; 把eax的第31bit復(fù)制到edx的每個bit上

? ? ? idiv dword ptr ds:[y]? ? ? ; eax = x / y

? ? ??

? ? ? invoke crt_printf,addr szFmt,eax

? ? ??

? ? main ENDP

END main

```


### 8.2 除數(shù)為正2的次冪(無符號)


如果除數(shù)是正數(shù)被除數(shù)也是正數(shù),且除數(shù)的范圍是正2的次冪,那么我們就可以使用`sar`算數(shù)右移指令來替代`div`除法指令,通過改變2的次冪的移位次數(shù)即可實(shí)現(xiàn)無符號除法的高速運(yùn)算。


?- 計算時我們需要參考次方表,這里我列舉出幾個常用的次方數(shù)值:

? ?- 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128

? ?- 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384


以下代碼中分別演示了除數(shù)為`2/4/8`三種計算方式,計算結(jié)果只需`sar`移位即可實(shí)現(xiàn)。

```ASM

.data

? x DWORD ?

? szFmt BYTE '計算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],5


? ? ; ----------------------------------------------------

? ? ; 【除數(shù)為2】

? ? ; 被除數(shù)為正數(shù)(無需擴(kuò)展): eax => 5 / 2 = 2

? ? mov eax,dword ptr ds:[x]? ?; 被除數(shù)

? ? sar eax,1? ? ? ? ? ? ? ? ? ; 算數(shù)右移1位

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; ----------------------------------------------------

? ? ; 【除數(shù)為4】

? ? ; 被除數(shù)為正數(shù)(無需擴(kuò)展): eax => 5 / 4 = 1

? ? mov eax,dword ptr ds:[x]

? ? sar eax,2? ? ? ? ? ? ? ? ? ; 算數(shù)右移2位

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; ----------------------------------------------------

? ? ; 【除數(shù)為8】

? ? ; 被除數(shù)為正數(shù)(無需擴(kuò)展): eax => 5 / 8 = 0

? ? mov eax,dword ptr ds:[x]

? ? sar eax,3? ? ? ? ? ? ? ? ? ; 算數(shù)右移3位

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? invoke ExitProcess,0

? main ENDP

END main

```


### 8.3 除數(shù)為負(fù)2的次冪(有符號)


如果除數(shù)是負(fù)數(shù),且除數(shù)范圍在負(fù)2的次冪內(nèi),那么在計算時應(yīng)使用`cdq`指令將被除數(shù)EAX擴(kuò)展為64位,并將擴(kuò)展后的結(jié)果放入`EDX:EAX`寄存器內(nèi),然后使用`sub eax,edx`減去高位符號位,接著通過`sar`算數(shù)右移指令完成除法運(yùn)算,最終通過`neg`指令將結(jié)果翻轉(zhuǎn)即可。


?- 總結(jié)計算過程如下:

? - 1.使用 `cdq` 指令將 `eax` 擴(kuò)展為64位,結(jié)果分別存入 `EDX:EAX` 寄存器內(nèi).

? - 2.使用 `sub eax,edx` 指令將高位符號位通過減法減掉.

? - 3.使用 `sar eax,x` 指令完成算數(shù)右移除法運(yùn)算.

? - 4.使用 `neg eax` 將計算后的正數(shù)反轉(zhuǎn)為負(fù)數(shù).


這個過程通過匯編語言實(shí)現(xiàn)代碼很簡單,如下代碼演示了除數(shù)為正數(shù)且被除數(shù)為 `-2/-4/-8` 次冪的計算過程.

```ASM

.data

? x DWORD ?

? szFmt BYTE '計算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],10


? ? ; 除數(shù)為(有符號)負(fù)2的次冪的計算過程

? ??

? ? ; 計算 10 / -2

? ? mov eax,dword ptr ds:[x]? ? ; x = 10

? ? cdq? ? ? ? ? ? ? ? ? ? ? ? ?; 符號擴(kuò)展 [edx:eax]

? ? sub eax,edx? ? ? ? ? ? ? ? ?; 減去符號位

? ? sar eax,1? ? ? ? ? ? ? ? ? ?; eax = 10 / -2

? ? neg eax? ? ? ? ? ? ? ? ? ? ?; 將正數(shù) eax 翻轉(zhuǎn)為負(fù)數(shù) = -5

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; 計算 10 / -4

? ? mov eax,dword ptr ds:[x]? ? ; x = 10

? ? cdq

? ? and edx,3

? ? add eax,edx

? ? sar eax,2? ? ? ? ? ? ? ? ? ?; eax = 10 / -4

? ? neg eax? ? ? ? ? ? ? ? ? ? ?; eax = -2

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; 計算 10 / -8

? ? mov eax,dword ptr ds:[x]

? ? cdq

? ? and edx,7

? ? add eax,edx

? ? sar eax,3

? ? neg eax

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? invoke ExitProcess,0

? main ENDP

END main

```


### 8.4 被除數(shù)為負(fù)數(shù)(有符號)


在有符號數(shù)的除法中,如果被除數(shù)為負(fù)數(shù),而除數(shù)是正2的次冪,那么可以使用`neg`取反操作來得到正確的計算結(jié)果。具體步驟如下:


- 首先,將被除數(shù)的絕對值與除數(shù)進(jìn)行除法運(yùn)算,并得到正確的商。

- 如果被除數(shù)為負(fù)數(shù),則對商進(jìn)行取反操作。

- 如果除數(shù)為負(fù)數(shù),則最終結(jié)果也要進(jìn)行取反操作。


例如,假設(shè)要計算`-27`除以`8`的值,我們可以按照如下步驟進(jìn)行計算:


?- 計算27除以8的值,得到商3和余數(shù)3。

?- 因?yàn)楸怀龜?shù)為負(fù)數(shù),所以對商取反,得到-3。

?- 因?yàn)槌龜?shù)為正數(shù),所以最終結(jié)果為-3,即-27/8的計算結(jié)果。


類似地,如果除數(shù)為負(fù)數(shù),我們需要在得到正確的計算結(jié)果后再進(jìn)行一次取反操作,這樣才能得到真正的結(jié)果。需要注意的是,上述方法僅適用于除數(shù)為正2的次冪的情況下。對于其他情況,需要使用更為復(fù)雜的算法來完成除法計算。


```ASM

.data

? x DWORD ?

? y DWORD ?

? szFmt BYTE '計算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],-10

? ? mov dword ptr ds:[y],-5


? ? ; 被除數(shù)為(有符號)的計算過程

? ??

? ? ; 計算 -10 / 2

? ? mov eax,dword ptr ds:[x]

? ? cdq

? ? sub eax,edx

? ? sar eax,1? ? ? ? ? ? ? ? ? ; eax = -10 / 2

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; 計算 -5 / 4

? ? mov eax,dword ptr ds:[y]

? ? cdq

? ? xor edx,edx

? ? add eax,edx

? ? sar eax,2? ? ? ? ? ? ? ? ? ; eax = -5 / 4

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; 計算 -10 / 8

? ? mov eax,dword ptr ds:[x]

? ? cdq? ? ? ? ? ? ? ? ? ? ? ? ?; 位擴(kuò)展

? ? xor edx,edx? ? ? ? ? ? ? ? ?; 清空高位

? ? add eax,edx

? ? sar eax,3? ? ? ? ? ? ? ? ? ?; eax = -10 / 8

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? invoke ExitProcess,0

? main ENDP

END main

```


### 8.5 除數(shù)與被除數(shù)均為負(fù)數(shù)(有符號)


在有符號數(shù)的除法中,如果除數(shù)和被除數(shù)均為負(fù)數(shù),且除數(shù)為負(fù)2的次冪,可以使用算數(shù)右移指令`sar`來完成除法運(yùn)算,然后通過取反指令`neg`來翻轉(zhuǎn)得到的計算結(jié)果的符號位。


具體來說,一個有符號整數(shù)除以負(fù)2的次冪,等價于這個有符號整數(shù)右移除數(shù)的位數(shù)作為移位數(shù),然后轉(zhuǎn)為無符號數(shù)進(jìn)行運(yùn)算,再將得到的無符號數(shù)轉(zhuǎn)回符號位正確的有符號數(shù)即可。由于右移的操作是算數(shù)右移,所以被移位的符號位會被保留。


例如,將`-16`除以`-8`,即計算`-16/-8`的結(jié)果,因?yàn)閌8`是`2`的`3`次冪,所以我們可以通過算數(shù)右移指令來完成除法,然后再取反獲得正確的結(jié)果:


?- 將-16右移3位,得到-2。

?- 對-2進(jìn)行取反,得到2。


因?yàn)閌-16`和`-8`均為負(fù)數(shù),所以最終結(jié)果也要進(jìn)行一次取反操作。因此,得到的結(jié)果為-2。


需要注意的是,上述方法僅適用于除數(shù)為負(fù)2的次冪,如果除數(shù)不是負(fù)2的次冪,則需要使用其他算法來計算除法運(yùn)算。


```ASM

.data

? x DWORD ?

? y DWORD ?

? z DWORD ?

? szFmt BYTE '計算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],-5

? ? mov dword ptr ds:[y],-24

? ? mov dword ptr ds:[z],-10

? ??

? ? ; 如果同時為負(fù)數(shù)的情況

? ??

? ? ; 計算 -5 / -2

? ? mov eax,dword ptr ds:[x]

? ? cdq? ? ? ? ? ? ? ? ? ? ? ? ; 符號擴(kuò)展

? ? xor edx,edx? ? ? ? ? ? ? ? ; 清空高位

? ? add eax,edx

? ? sar eax,1? ? ? ? ? ? ? ? ? ; 算數(shù)右移動 -5 / -2

? ? neg eax? ? ? ? ? ? ? ? ? ? ; eax = 3 (負(fù)負(fù)得正)

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; 計算 -24 / -4

? ? mov eax,dword ptr ds:[y]

? ? cdq? ? ? ? ? ? ? ? ? ? ? ? ; 符號擴(kuò)展

? ? xor edx,edx? ? ? ? ? ? ? ? ; 清空高位

? ? add eax,edx

? ? sar eax,2? ? ? ? ? ? ? ? ? ; 算數(shù)右移動 -24 / -4

? ? neg eax? ? ? ? ? ? ? ? ? ? ; eax = 6 (負(fù)負(fù)得正)

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; 計算 -10 / -8

? ? mov eax,dword ptr ds:[z]? ? ; z = -10?

? ? cdq? ? ? ? ? ? ? ? ? ? ? ? ?; 符號擴(kuò)展

? ? xor edx,edx? ? ? ? ? ? ? ? ?; 清空高位

? ? add eax,edx

? ? sar eax,3? ? ? ? ? ? ? ? ? ?; eax = -10 / -8?

? ? neg eax? ? ? ? ? ? ? ? ? ? ?; eax = 1 (負(fù)負(fù)得正)

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? invoke ExitProcess,0

? main ENDP

END main

```

如上我們所有的除法運(yùn)算中,無論是有符號還是無符號都在進(jìn)行2的次冪運(yùn)算,通常針對2的次冪運(yùn)算并不需要經(jīng)過特殊的`模M`計算,而對于非2次冪`3/5/7`的運(yùn)算,則需要通過一定的公式才能簡化計算過程,如下將開始介紹非2次冪除法運(yùn)算該如何優(yōu)化。


### 8.6 除數(shù)為正非2次冪(有符號)


對于除數(shù)為正非2次冪的有符號數(shù),我們需要使用其他的算法來完成除法運(yùn)算。通常情況下,可以使用恒等式轉(zhuǎn)化法或移位除法來進(jìn)行計算。


一種常用的移位除法算法是:


?- 將被除數(shù)與除數(shù)分別取絕對值,并記錄下符號。

?- 如果除數(shù)大于被除數(shù),則直接返回0。

?- 通過不斷將除數(shù)左移,直到左移之后的除數(shù)大于等于被除數(shù),得到最高位的不為0的位數(shù),記為n。

?- 將除數(shù)左移n位,然后不斷依次將左移后的除數(shù)與被除數(shù)相減,直到被除數(shù)小于除數(shù)。

?- 記錄下相減的次數(shù),即為最終的商。


上述算法僅適用于除數(shù)為正數(shù)的情況。如果除數(shù)為負(fù)數(shù),則需要先取反,然后使用移位除法的算法來計算除法運(yùn)算,并最終再取反,以得到正確的計算結(jié)果。


關(guān)于求解公式`2^(32+n) / M`的使用方法:可以通過移位和除法結(jié)合的方法來計算,具體可以按照以下步驟進(jìn)行計算:


?- 將除數(shù)M保存在寄存器中,將`32+n`的值保存在寄存器中。

?- 執(zhí)行左移指令,將`32+n`左移至最高位。將左移后的值保存在另一個寄存器中。

?- 執(zhí)行除法指令,將左移后的值除以M,得到商和余數(shù)。

?- 如果余數(shù)不為0,則重新計算`32+n+1`的值,再次執(zhí)行上述步驟。


這樣,不斷重復(fù)這個過程,就可以計算出`2^(32+n) / M`的結(jié)果。


先來看一段匯編代碼,我們此時已知 `M = 055555556h 且 edx = N` 帶入公式 `2^(32+N) / M` 由于`edx`沒有變化所以此處應(yīng)計算 `2^32 / 055555556h = 2.9999` 即可計算出此處的除數(shù)是`3`,而被除數(shù)則是`ecx`寄存器內(nèi)的值,我們即可得知該段匯編指令在進(jìn)行 `ecx / 3` 的計算流程。

```ASM

mov ecx,dword ptr ds:[y]? ? ? ; 被除數(shù)

mov eax,055555556h? ? ? ? ? ? ; M值 => 此處的M模值是編譯器計算后得到的(我們無需深入理解)

imul ecx

mov eax,edx? ? ? ? ? ? ? ? ? ?; edx = N

shr eax,01fh

add edx,eax

invoke crt_printf,addr szFmt,edx

```

再來看另一段,這段代碼中 `sar edx,1` 此時`edx`的值發(fā)生過一次變化變換了1次,所以公式中應(yīng)該加上變化的一次計算得到 `2^33 / 66666667 = 5` 所以可得到當(dāng)前除數(shù)是`5`

```ASM

mov ecx,dword ptr ds:[y]? ? ? ?; ecx = 10 / 5 = 2

mov eax,066666667h? ? ? ? ? ? ?; 此處的M模值是編譯器計算后得到的

imul ecx

sar edx,1? ? ? ? ? ? ? ? ? ? ? ; 想要知道除數(shù)是多少,只需要執(zhí)行以下計算

mov eax,edx? ? ? ? ? ? ? ? ? ? ; 2^(32 + edx) / M = 2^33 / 66666667 = 5

shr eax,01fh? ? ? ? ? ? ? ? ? ?; 33次方的由來,其實(shí)是默認(rèn)的32次方加上 sar edx,1 中的1次方得到的

add edx,eax

invoke crt_printf,addr szFmt,edx

```

針對除數(shù)為非2的次冪且為有符號數(shù),只需要提供對應(yīng)的M模值,根據(jù)模值即可將對應(yīng)的除法轉(zhuǎn)換為乘法,一般寫法如下:

```ASM

.data

? x DWORD ?

? szFmt BYTE '計算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],10

? ??

? ? ; 除法(無符號)非2的冪轉(zhuǎn)換為乘法

? ??

? ? ; 計算 10 / 3

? ? mov ecx,dword ptr ds:[x]? ? ? ; 被除數(shù) ecx = 10 / 3 = 3

? ? mov eax,055555556h? ? ? ? ? ? ; eax = M值 1431655766

? ? imul ecx

? ? mov eax,edx? ? ? ? ? ? ? ? ? ?; edx = n 計算: 2^(32+n) / M

? ? shr eax,01fh? ? ? ? ? ? ? ? ? ; 計算出除數(shù)為 2.9999 => 3

? ? add edx,eax

? ? invoke crt_printf,addr szFmt,edx

? ??

? ? ; 計算 10 / 5

? ? mov ecx,dword ptr ds:[x]? ? ? ?; ecx = 10 / 5 = 2

? ? mov eax,066666667h? ? ? ? ? ? ?; 此處的M模值是編譯器計算后得到的

? ? imul ecx

? ? sar edx,1? ? ? ? ? ? ? ? ? ? ? ; 想要知道除數(shù)是多少,只需要

? ? mov eax,edx? ? ? ? ? ? ? ? ? ? ; 2^(32 + edx) / M = 2^33 / 66666667 = 5

? ? shr eax,01fh? ? ? ? ? ? ? ? ? ?; 邏輯右移

? ? add edx,eax

? ? invoke crt_printf,addr szFmt,edx

? ??

? ? ; 計算 10 / 6

? ? mov ecx,dword ptr ds:[x]? ? ? ?; ecx = 10 / 6 = 1

? ? mov eax,02AAAAAABh? ? ? ? ? ? ?; eax = 715827883

? ? imul ecx

? ? mov eax,edx? ? ? ? ? ? ? ? ? ? ; 2^(32 + edx) / M = 2^32 / 2AAAAAAB = 6

? ? shr eax,01fh

? ? add edx,eax

? ? invoke crt_printf,addr szFmt,edx

? ??

? ? invoke ExitProcess,0

? main ENDP

END main

```


### 8.7 除數(shù)為正非2次冪(無符號)


在上方代碼中的除法計算是針對有符號數(shù)進(jìn)行的,如果是針對無符號數(shù)則需要另一種計算方式,對于除數(shù)為正非2次冪的無符號數(shù),這里介紹一種常用的算法,恒等式轉(zhuǎn)化法。


假設(shè)我們需要計算一個`64`位無符號整數(shù)`x`除以一個`32`位無符號整數(shù)`y`的值,我們可以按照以下步驟進(jìn)行計算:


?- 計算`2^32/y`的低`32`位,假設(shè)得到的結(jié)果為k,即`k = floor(2^32/y)` 。

?- 將x的高32位和低32位分別除以y,并將商的高32位保存下來,記為q1,即`q1 = floor(high_32_bits(x) / y) `。

?- 將q1乘以k,并將結(jié)果右移32位,得到`kq1`的高32位,記為q2,即`q2 = floor( k * q1 / 2^32 )` 。

?- 將x的低32位與被除數(shù)的乘積減去 q2 乘以y的值就是x除以y的值,即`(floor(x * k / 2^32) - q2) * y + x mod y`。


以上步驟可以用以下公式來表示:


```c

x / y = [(floor(high_32_bits(x) / y) * floor(2^32 / y) + floor(k * floor(high_32_bits(x) / y) / 2^32) * 2^32) * y + x mod y] / y

```


其中,`high_32_bits(x)`表示x的高32位,`floor()`表示向下取整,mod表示取余數(shù)。


需要注意,上述算法僅適用于除數(shù)為正數(shù)的情況。如果除數(shù)為負(fù)數(shù),則需要先將除數(shù)取反,然后使用恒等式轉(zhuǎn)化法的算法來計算除法運(yùn)算,并最終再取反,以得到正確的計算結(jié)果。


```ASM

.data

? x DWORD ?

? y DWORD ?

? z DWORD ?

? szFmt BYTE '計算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],-5

? ? mov dword ptr ds:[y],10

? ? mov dword ptr ds:[z],20

? ??

? ? ; 除法(無符號)非2的次冪(正數(shù))轉(zhuǎn)換為乘法

? ? xor edx,edx

? ? mov ecx,dword ptr ds:[y]? ? ; ecx = 10

? ? mov eax,0AAAAAAABh? ? ? ? ? ; ecx / 3 = 3

? ? mul ecx

? ? shr edx,1

? ? invoke crt_printf,addr szFmt,edx

? ??

? ? ; 還原除數(shù): 2 ^(32 + n) / M => 2 ^ (32+2) / 0CCCCCCCDh = 5

? ? xor edx,edx

? ? mov ecx,dword ptr ds:[y]? ? ; ecx = 10 => 計算: 10/5

? ? mov eax,0CCCCCCCDh? ? ? ? ? ; eax = M

? ? mul ecx

? ? shr edx,2? ? ? ? ? ? ? ? ? ?; edx= n

? ? invoke crt_printf,addr szFmt,edx

? ??

? ? ; 還原除數(shù): 2 ^(32 + n) / M => 2 ^ (32+2) / 0AAAAAAABh = 6

? ? xor edx,edx

? ? mov ecx,dword ptr ds:[y]? ? ?; ecx = 10 => 計算:10/6

? ? mov eax,0AAAAAAABh? ? ? ? ? ?; eax = M

? ? mul ecx

? ? shr edx,2? ? ? ? ? ? ? ? ? ? ; edx = n

? ? invoke crt_printf,addr szFmt,edx

? ??

? ? ;還原除數(shù): 2 ^(32 + n) / M => 2 ^ 33 / 038E38E39h = 9

? ? xor edx,edx

? ? mov ecx,dword ptr ds:[z]? ? ?; ecx = 20? => 計算: 20/9

? ? mov eax,038E38E39h? ? ? ? ? ?; eax = M

? ? mul ecx

? ? shr edx,1? ? ? ? ? ? ? ? ? ? ; edx = n

? ? invoke crt_printf,addr szFmt,edx

? ? invoke ExitProcess,0

? main ENDP

END main

```


### 8.8 除數(shù)為負(fù)非2次冪(無符號)


如果我們的除數(shù)是一個負(fù)數(shù),且范圍是非2的次冪,那么當(dāng)我們計算結(jié)束后,只需要在原來基礎(chǔ)上多增加一個`neg`將結(jié)果翻轉(zhuǎn)以下即可。


采用與有符號整數(shù)的移位除法類似的方法,分為兩個階段完成。


?- 階段1:將除數(shù)和被除數(shù)分別取絕對值,并計算出商的符號。由于除數(shù)為負(fù)數(shù),所以商的符號為負(fù)號。

?- 階段2:使用移位除法算法(詳見上述有符號數(shù)除法的算法),計算出無符號整數(shù)的商。


最后,因?yàn)樯虨樨?fù)數(shù),所以需要將其翻轉(zhuǎn)一下,即執(zhí)行一次取反指令`neg`,以得到正確的計算結(jié)果。


```ASM

.data

? x DWORD ?

? y DWORD ?

? szFmt BYTE '計算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],10

? ? mov dword ptr ds:[y],20

? ??

? ? ; 還原除數(shù): 2 ^(32 + n) / M => 2 ^ 33 / 0AAAAAAABh = nge(3) => -3

? ? xor edx,edx

? ? mov ecx,dword ptr ds:[y]? ? ? ; ecx = 20? => 計算: 20 / -3

? ? mov eax,0AAAAAAABh? ? ? ? ? ? ; eax = M

? ? mul ecx

? ? shr edx,1? ? ? ? ? ? ? ? ? ? ?; edx = n?

? ? neg edx? ? ? ? ? ? ? ? ? ? ? ?; edx=6 結(jié)果neg取反

? ? invoke crt_printf,addr szFmt,edx

? ??

? ? ; 還原除數(shù): 2 ^(32 + n) / M => 2 ^ 62 / 040000001h = 4294967292

? ? xor edx,edx

? ? mov ecx,dword ptr ds:[x]? ? ? ?; ecx = 10 => 計算: 10 / -3

? ? mov eax,040000001h? ? ? ? ? ? ?; eax = M

? ? mul ecx

? ? shr edx,01eh? ? ? ? ? ? ? ? ? ?; edx = n

? ? invoke crt_printf,addr szFmt,edx

? ??

? ? invoke ExitProcess,0

? main ENDP

END main

```

而如果反過來,`被除數(shù)變成負(fù)數(shù)`,而除數(shù)則還是非2的次冪,那么計算方式應(yīng)該如下所示:

```ASM

.data

? x DWORD ?

? szFmt BYTE '計算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],-10

? ??

? ? ; 除法(有符號)非2的冪轉(zhuǎn)換為乘法

? ? mov ecx,dword ptr ds:[x]? ? ? ?; ecx = -10 / 9 = -1

? ? mov eax,038E38E39h? ? ? ? ? ? ?; eax = 954437177?

? ? imul ecx

? ? sar edx,1? ? ? ? ? ? ? ? ? ? ? ; 2^(32 + edx) / M = 2^33 / 38E38E39 = 9

? ? mov ecx,edx

? ? shr ecx,01fh

? ? add edx,ecx

? ? invoke crt_printf,addr szFmt,edx

? ??

? ? invoke ExitProcess,0

? main ENDP

END main

```


本文作者: 王瑞

本文鏈接: https://www.lyshark.com/post/1f99ad3b.html

版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉(zhuǎn)載請注明出處!


5.8 匯編語言:匯編高效除法運(yùn)算的評論 (共 條)

分享到微博請遵守國家法律
锡林浩特市| 盐山县| 容城县| 塔城市| 大冶市| 南京市| 迭部县| 蒙阴县| 徐汇区| 湘潭市| 且末县| 沂南县| 元江| 兰西县| 呼图壁县| 太仆寺旗| 类乌齐县| 哈巴河县| 平谷区| 禹城市| 乌恰县| 米泉市| 乃东县| 关岭| 河津市| 连城县| 百色市| 宣威市| 汉阴县| 凤翔县| 八宿县| 东方市| 白水县| 阿拉尔市| 英吉沙县| 若尔盖县| 喀喇| 湟源县| 永昌县| 孝感市| 溧阳市|