說明:除法相對其他幾種運算的優化又要更難了,主要是其包含的數學知識(離散數學?)
由於我本人比較笨,也不喜歡深究數學,所以盡量跳過數學,以更直觀的方式來解釋(其實也就是我自己的理解方法了)
取整分為向上取整和向下取整以及向0取整,我們在程式語言中的取整都是向0取整,但是有一些運算卻是向下取整的,比如右移運算就是向下取整的,所以在負數的時候,為了保證向0取整,就會做一些調整。
說明:分為被除數大於0和小於0兩種情況討論。
mov eax, val
cdq ;符號擴充套件,eax符號位為1,設edx為-1,否則設edx為0
sub eax, edx
sar eax, 1
這段**是x / 2的**,使用cdq避免了分支。
當x >= 0, edx為0,sub eax, edx沒有作用,直接右移1位
當x < 0,edx為-1, sub eax, edx使得eax = eax + 1(在這裡由於2^1-1 == 1所以沒有後續操作),然後移位
另外乙個示例:
mov eax, val
cdqand edx, 7
add eax, edx
sar eax, 3
這段**是x/8的**。
當x >= 0時,cdq使得edx為0,and和add操作沒有作用,sar移位直接完成操作。
當x < 0時,cdq使得edx為-1,即全f,全f和7按位和的結果是保留7的結果,於是add完成了eax = eax + 2^3 - 1即eax = eax + 7的操作,最後移位完成運算。 由於8是常量,所以這裡的2^n - 1是可以提前算出來的。
說明:
示例1:
;ecx 為某通用暫存器(可替換)
mov ecx, val
mov eax, 38e38e39h
imul ecx ;有符號乘法,用eax乘ecx,edx存放高4位元組,eax存放低4位元組
sar edx, 1
;有符號移位
mov eax, edx
shr eax, 1fh ;無符號移位
add edx, eax
這段**是x/9的**。令magic number m = 38e38e39h
對於x>=0時,eax = eax*m,相當於完成了x * (2^n/y)的操作,我們現在暫時還不知道n具體是多少。imul之後使用的是edx,edx為高4位元組,之後eax棄用,說明用edx記錄結果,相當於使用了右移了32位的值,加之sar的右移1位,所以移位了33位。然後將eax用來記錄該值右移33位之後再右移1f(31)位。之後的右移31位即獲取到了sar之後edx的符號位,用eax存,大於0,為0,所以add不做操作。由此,移動了共33位,即n=33,m = 2^n / y = 2^33 / y = 38e38e39h,可得y約等於9。
對於x<0時,eax將會得到符號位,小於0,eax為符號位,也就是1,所以add使得結果+1。
更難一點的示例2:
;ecx為某通用暫存器(可替換)
mov eax, 24924925h
mul ecx
sub ecx, edx
shr ecx, 1
add ecx, edx
shr ecx, 2
這段**是x/7的**。和上一例的區別在於,magic number的計算得出的結果超出了乙個四位元組整數的範圍,所以對其做出了調整,導致之後的步驟也做出了調整,但總體思路一致。另外還要注意,由於是mul,這裡的計算是無符號計算,可以認為都是正數,沒有負數處理的部分。
首先m = 24924925h,剩下的部分就需要用筆算了,因為涉及到化簡,我在這裡不再贅述,基本上就是把等效的表示式寫出來然後化簡,最後結果為ecx = ecx0 * (2^32+m) /2^35,ecx0為最初的ecx(除數)。根據之前的原理,如圖有:
所以m其實是(2^32 * 2^n/y) n為3,就符合之前得到的運算結果了。
根據n和m即可算出來y = 7了。
示例3:
;ecx 為某通用暫存器(可替換為其他)
mov eax, 92492493h(大於0x7fffffff)
imul ecx
add edx, ecx
sar edx, 2
mov eax, edx
shr eax, 1fh
add edx, eax
這裡也是x/7的**。但是是有符號運算。
乙個細節:對於有符號運算,大於0x8000 0000的數值,會被認為是負數,其值為 val-2^32,val為對應無符號數值。因此對公式做出相應變形:
由此分析**
首先我們應該通過示例1,可以識別
mov eax, edx
shr eax, 1fh
add edx, eax
是在針對負數做調整,所以關注點為前面幾行。
根據公式的變形,magic number現在是(2^n/y-2^32),加之對**的公式進行轉換,可以得到
edx = (ecx*eax+2^32*ecx)/2^34 符合我們的形式,所以即可得到最終為x/7.
對於除數大於0的情況,首先我們得認識到乙個取整的公式,就是將被除數為負的情況的向下取整變成向上取整,然後其餘的情況就簡單了。 分析的方法基本上就是把彙編**對應的表示式寫出來並化簡,然後通過對公式的恒等變形,盡力化成表示式的形式,對於負數,一般是在彙編**中設法獲得符號位然後做乙個加法。
shell程式設計之正式表示式
二 文字處理器 正規表示式又稱正規表示式 常規表示式。在 中常簡寫為 regex regexp 或 re。正規表示式是使用單個字串來描述 匹配一系列符合某個句法規則的字串,簡單來說,是 一種匹配字串的方法,通過一些特殊符號,實現快速查詢 刪除 替換某個特定字串 正規表示式的字串表達方法根據不同的嚴謹...
正規表示式優化
正規表示式的優化 在jeffrey e.f.friedl 的 精通正規表示式 中提到了幾種技巧。今天著重說一種比較實用的。比較簡單的 在類似 或者 s s 中匹配的時候,量詞 預設是貪婪的,啟用最大匹配模式,會匹配到盡量多的字串,如果我們的需求是匹配text中的text,這樣就不適用了。具體來說,我...
正規表示式優化
正規表示式的優化 在jeffrey e.f.friedl 的 精通正規表示式 中提到了幾種技巧。今天著重說一種比較實用的。比較簡單的 在類似 或者 s s 中匹配的時候,量詞 預設是貪婪的,啟用最大匹配模式,會匹配到盡量多的字串,如果我們的需求是匹配text中的text,這樣就不適用了。具體來說,我...