從匯編代碼中探究整數(shù)的除法運(yùn)算
前言
看過我的匯編語言101的人應(yīng)該都知道,指令div D或者idiv D是用于做除法的指令(具體的過程可以參考我的匯編語言101視頻)
例如:
是 n/5 的指令,運(yùn)算結(jié)果會(huì)儲存在eax中。
但是,以上的Assembly最多只會(huì)在-O0見到,如果把優(yōu)化等級調(diào)到最高的-O3,你基本上見不到div這個(gè)指令。
此外,我對電路設(shè)計(jì)不是很了解,如果你想要知道CPU是如何做除法運(yùn)算的,可以參考這里:https://electronics.stackexchange.com/questions/22410/how-does-division-occur-in-our-computers
*注意:本文之后所有數(shù)字,如果在末尾加b,則代表這是一個(gè)2進(jìn)制下的數(shù),否則就是十進(jìn)制下的數(shù)

一、n/1
我們先從最簡單的n/1開始,不用說,n/1那就是n,如果是-O0,編譯器會(huì)生產(chǎn)如下的匯編代碼
這就是沒有優(yōu)化前的代碼,我們說什么,編譯器做什么。如果是-O3的話,編譯器會(huì)直接返回n,即,直接忽略我們的除法運(yùn)算。
本文之后的內(nèi)容都以-O3為標(biāo)準(zhǔn),不在談及-O0

二、整數(shù)除以2的n次方
接下來是非負(fù)整數(shù)除以2,眾所周知,我們的計(jì)算機(jī)用bit shift做整數(shù)的除法運(yùn)算,比如說
這個(gè)正數(shù)的除法運(yùn)算對應(yīng)的assembly:
把所有bit往右移動(dòng)1的距離,比如1011b移動(dòng)后就變成了0101b,十進(jìn)制下的11就變成了5。
同樣的,如果我們要除以4,那么我們就把eax朝右移動(dòng)2位,除以8就移動(dòng)3位。
但是注意,如果我們bit位移量大于我們數(shù)據(jù)類型的長度的話,會(huì)出現(xiàn)什么呢?比如說,讓一個(gè)word類型的數(shù)據(jù)除以2的17次方,這個(gè)時(shí)候,我們的程序不會(huì)再進(jìn)行bit位移,而是直接返回0
(xor自己本身就是將自己清零)
但是等等,這個(gè)并不是故事的全部,上面的是非負(fù)整數(shù),如果是整數(shù)的話,我們的代碼還會(huì)再多2行:
我們在這里做得運(yùn)算,用公式寫出來就是
引入n SHR 31的作用顯而易見,就是n為負(fù)數(shù)的情況下——比如n=-2時(shí),bit右移31位后會(huì)獲得1b,然后二者相加得到-1,然后做bit右移1位同時(shí)sign填充后獲得-1;如果是在n為正數(shù)的情況下,n SHR 31這個(gè)數(shù)直接被忽略掉了。

三、整數(shù)除以整數(shù)
接下來我們來看一下整數(shù)與整數(shù)的除法運(yùn)算,我們先假定我們的兩個(gè)數(shù)都是非負(fù)數(shù),如一個(gè)非負(fù)整數(shù)n除以5,將會(huì)得到如下的匯編代碼:
我們還是沒有看見div的身影。上面的代碼稍微有點(diǎn)復(fù)雜,但是我們本質(zhì)上就是,將n的值乘以3435973837,然后bit右移34位(注意此處rax長度為64)
我們在這里可以隨便找一個(gè)數(shù)試一試,比如n=123:
1100010 01100110 01100110 01100110 01111111b
右移后我們得到11000b,即24;而123/5的結(jié)果則是24.6,去掉小數(shù)點(diǎn)我們得到24
我們試著把3435973837這個(gè)數(shù)字去除以2^34,我們會(huì)得到非常接近0.2的數(shù),準(zhǔn)確的說是0.2后面很多個(gè)0然后一個(gè)1。
我們現(xiàn)在取消我們非負(fù)數(shù)的限制,同樣的n/5我們會(huì)得到以下的代碼
因?yàn)樵谶@里我們涉及到正負(fù)號,所以我們有movsx和sar兩個(gè)和符號有關(guān)的指令,并且最后一步我們居然是做一個(gè)減法運(yùn)算。用公式表達(dá)就是:
我們可以理解為n*0.2然后減去一個(gè)非常小的數(shù) n SAR 31;為什么是這個(gè)組合,我不知道,但是nSAR31似曾相識?我們上面那一節(jié)里面貌似也有一個(gè)n?SHR?31?

以上就是3種不同情況下的除法(如果是浮點(diǎn)型數(shù)據(jù)的除法的話,我們不可避免地還是會(huì)用到divss這個(gè)指令)
下一篇文章我會(huì)再和大家來講講取余運(yùn)算,劇透一下,還是沒有div這個(gè)指令。
參考工具:Compiler Explorer: https://godbolt.org/

THE END.