除法运算效率在四种基本运算中最慢,是乘法的20倍左右。所以在被除数为固定值时(重要),编译器会把除法优化乘法。
设代码a/b = c b是常量
设b*k = 2^n 2的n次直接用移位运算即可
a *2^n/ (b*2 ^n) = c
a * b *k/b = c* 2^n
a *k >> n = c
所以a *k >> n = a/b //编译器会选择n的长度 从而保证后面位数舍弃时,造成的影响最小
下面让我们看一下代码
006A0200 /$ 8B41 04 mov eax, dword ptr [ecx+4] ;
0F0C78A8 006A0203 |. 85C0 test eax, eax
006A0205 |. 75 01 jnz short 006A0208
006A0207 |. C3 retn
006A0208 |> 8B49 08 mov ecx, dword ptr [ecx+8] ; 0F0C7938
006A020B |. 2BC8 sub ecx, eax
006A020D |. B8 398EE338 mov eax, 38E38E39
006A0212 |. F7E9 imul ecx ; EAX*ECX 低位放EAX 高位放EDX 006A0214 |. C1FA 01 sar edx, 1 ; 右移1位
006A0217 |. 8BC2 mov eax, edx
006A0219 |. C1E8 1F shr eax, 1F
006A021C |. 03C2 add eax, edx
006A021E \. C3 retn
a * 0x38E38E39 >> 33 = a / 9
这个值要大于2^33 所以求值时2^n / b 要向上取整
b *k = 0x38E38E39*9= 0x20000 0001=(2^(32+1))+1
上述代码:直接取edx相当于>>32,然后再sar edx,1,总体右移动了33位
下图为将除法转换为乘法的MagicNumber,从中也可以找到当b=9时,对应的MagicNumber为38E38E39。

网友评论