1、 除法
对计算机而言,整数除法的结果必须是整数。计算机取整数部分的方式有如下几种:
① 向下取整
如:-3.5=>-4;3.5=>3;
② 向上取整
如:-3.5=>-3;3.5=>4;
③ 向零取整
如:-3.5=>-3;3.5=>3;
C++和大多数高级语言,对整数除法都规定向零取整。
整数除法的几种情况:
① 常量除以常量
② 变量除以常量(常量值为2的幂)
③ 变量除以常量(常量值为非2的幂)
④ 变量除以常量(常量值为负的2的幂)
⑤ 变量除以常量(常量值为负的非2的幂)
⑥ 变量除以变量
//③和⑤未在代码中进行详细分析,因为有点复杂,搞不太懂优化规则。看最后的总结
C++原码
Debug版
Release版
#include <iostream>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
//变量除以常量(常量值为2的幂)
a = a / 2;
cout << a ;
a = a / 8;
cout << a;
//变量除以常量(常量值为负的2的幂)
a = a / -2;
cout << a;
a = a / -8;
cout << a;
//变量除以常量(常量值为非2的幂)
a = a / 15;
cout << a;
//
a = a / 11;
cout << a;
//变量除以常量(常量值为负的非2的幂)
a = a / -15;
cout << a;
//
a = a / -11;
cout << a;
//常量除以常量
a = 18 / 15;
cout << a;
//变量除以变量
a = a / b;
cout << a;
system("pause");
return 0;
}
#include <iostream>
using namespace std;
int main()
{
.....略
int a, b;
cin >> a >> b;
..........略
//变量除以常量(常量值为2的幂)
a = a / 2;
00F75EB6 mov eax,dword ptr [a]
00F75EB9 cdq
00F75EBA sub eax,edx
00F75EBC sar eax,1
00F75EBE mov dword ptr [a],eax
cout << a ;
00F75EC1 mov esi,esp
cout << a ;
00F75EC3 mov eax,dword ptr [a]
00F75EC6 push eax
00F75EC7 mov ecx,dword ptr ds:[0F810A8h]
00F75ECD call dword ptr ds:[0F81094h]
00F75ED3 cmp esi,esp
00F75ED5 call __RTC_CheckEsp (0F71339h)
a = a / 8;
00F75EDA mov eax,dword ptr [a]
00F75EDD cdq
00F75EDE and edx,7
00F75EE1 add eax,edx
00F75EE3 sar eax,3
00F75EE6 mov dword ptr [a],eax
cout << a;
00F75EE9 mov esi,esp
00F75EEB mov eax,dword ptr [a]
00F75EEE push eax
00F75EEF mov ecx,dword ptr ds:[0F810A8h]
00F75EF5 call dword ptr ds:[0F81094h]
00F75EFB cmp esi,esp
00F75EFD call __RTC_CheckEsp (0F71339h)
//变量除以常量(常量值为负的2的幂)
a = a / -2;
00F75F02 mov eax,dword ptr [a]
00F75F05 cdq
00F75F06 sub eax,edx
00F75F08 sar eax,1
00F75F0A neg eax
00F75F0C mov dword ptr [a],eax
cout << a;
00F75F0F mov esi,esp
00F75F11 mov eax,dword ptr [a]
00F75F14 push eax
00F75F15 mov ecx,dword ptr ds:[0F810A8h]
cout << a;
00F75F1B call dword ptr ds:[0F81094h]
00F75F21 cmp esi,esp
00F75F23 call __RTC_CheckEsp (0F71339h)
a = a / -8;
00F75F28 mov eax,dword ptr [a]
00F75F2B cdq
00F75F2C and edx,7
00F75F2F add eax,edx
00F75F31 sar eax,3
00F75F34 neg eax
00F75F36 mov dword ptr [a],eax
cout << a;
00F75F39 mov esi,esp
00F75F3B mov eax,dword ptr [a]
00F75F3E push eax
00F75F3F mov ecx,dword ptr ds:[0F810A8h]
00F75F45 call dword ptr ds:[0F81094h]
00F75F4B cmp esi,esp
00F75F4D call __RTC_CheckEsp (0F71339h)
//变量除以常量(常量值为非2的幂)
a = a / 15;
00F75F52 mov eax,dword ptr [a]
00F75F55 cdq
00F75F56 mov ecx,0Fh
00F75F5B idiv eax,ecx
00F75F5D mov dword ptr [a],eax
cout << a;
00F75F60 mov esi,esp
00F75F62 mov eax,dword ptr [a]
00F75F65 push eax
00F75F66 mov ecx,dword ptr ds:[0F810A8h]
00F75F6C call dword ptr ds:[0F81094h]
00F75F72 cmp esi,esp
00F75F74 call __RTC_CheckEsp (0F71339h)
//
a = a / 11;
00F75F79 mov eax,dword ptr [a]
00F75F7C cdq
00F75F7D mov ecx,0Bh
00F75F82 idiv eax,ecx
00F75F84 mov dword ptr [a],eax
cout << a;
00F75F87 mov esi,esp
00F75F89 mov eax,dword ptr [a]
00F75F8C push eax
00F75F8D mov ecx,dword ptr ds:[0F810A8h]
00F75F93 call dword ptr ds:[0F81094h]
00F75F99 cmp esi,esp
00F75F9B call __RTC_CheckEsp (0F71339h)
//变量除以常量(常量值为负的非2的幂)
a = a / -15;
00F75FA0 mov eax,dword ptr [a]
00F75FA3 cdq
00F75FA4 mov ecx,0FFFFFFF1h
00F75FA9 idiv eax,ecx
00F75FAB mov dword ptr [a],eax
cout << a;
00F75FAE mov esi,esp
00F75FB0 mov eax,dword ptr [a]
00F75FB3 push eax
00F75FB4 mov ecx,dword ptr ds:[0F810A8h]
00F75FBA call dword ptr ds:[0F81094h]
00F75FC0 cmp esi,esp
00F75FC2 call __RTC_CheckEsp (0F71339h)
//
a = a / -11;
00F75FC7 mov eax,dword ptr [a]
00F75FCA cdq
00F75FCB mov ecx,0FFFFFFF5h
00F75FD0 idiv eax,ecx
00F75FD2 mov dword ptr [a],eax
cout << a;
00F75FD5 mov esi,esp
00F75FD7 mov eax,dword ptr [a]
00F75FDA push eax
00F75FDB mov ecx,dword ptr ds:[0F810A8h]
00F75FE1 call dword ptr ds:[0F81094h]
00F75FE7 cmp esi,esp
00F75FE9 call __RTC_CheckEsp (0F71339h)
//常量除以常量
a = 18 / 15;
00F75FEE mov dword ptr [a],1
cout << a;
00F75FF5 mov esi,esp
00F75FF7 mov eax,dword ptr [a]
00F75FFA push eax
00F75FFB mov ecx,dword ptr ds:[0F810A8h]
00F76001 call dword ptr ds:[0F81094h]
00F76007 cmp esi,esp
00F76009 call __RTC_CheckEsp (0F71339h)
//变量除以变量
a = a / b;
00F7600E mov eax,dword ptr [a]
00F76011 cdq
00F76012 idiv eax,dword ptr [b]
00F76015 mov dword ptr [a],eax
cout << a;
00F76018 mov esi,esp
00F7601A mov eax,dword ptr [a]
00F7601D push eax
00F7601E mov ecx,dword ptr ds:[0F810A8h]
00F76024 call dword ptr ds:[0F81094h]
00F7602A cmp esi,esp
00F7602C call __RTC_CheckEsp (0F71339h)
system("pause");
............略
return 0;
00F76048 xor eax,eax
}
...........略
#include <iostream>
using namespace std;
int main()
{
//...........略
int a, b;
cin >> a >> b;
011712B0 mov ecx,dword ptr ds:[1173038h]
int a, b;
cin >> a >> b;
//................略
//变量除以常量(常量值为2的幂)
a = a / 2;
011712CC mov eax,dword ptr [a]
cout << a ;
011712CF mov ecx,dword ptr ds:[117303Ch]
//用eax的符号位值填充edx,即如果eax(变量a)的值是负数,则edx=0XFFFFFFFF=-1,否则edx=0x0=0;
011712D5 cdq
//因为有符号除法的规则是向0取整,所以如果变量a是负数,则a+1(eax-edx=a-(-1)=a+1),否则a+0(eax-edx=a-0)不做处理,最后进行左移。
//例如:a=5(0x00000101),右移1位(a/2)得a=2;
//a=-5(0x11111011),如果直接右移1位(a/2)得a=0x11111101=-3,而向零取整的正确结果是-2,所以算数移位指令等价与向下取整,因此被除数为负数时,先对其进行调整(a+(2的n次方-1)),再进行移位。
//a+(2的1次方-1)=-5+1=-4(0X11111100),右移1位得a=-2(0X11111110)
011712D6 sub eax,edx
011712D8 sar eax,1
011712DA push eax
011712DB mov dword ptr [a],eax
011712DE call dword ptr ds:[1173024h]
a = a / 8;
011712E4 mov eax,dword ptr [a]
cout << a;
011712E7 mov ecx,dword ptr ds:[117303Ch]
011712ED cdq
//如果a为负,则eax=-1,然后eax&7得eax=7(8=2的3次方,然后减1),然后eax+edx,最后进行移位
//例如:a=-9(0x11110111),如果直接右移3位(除以8)得a=-2(0x11111110),而我们要的结果是-1,所以a+7=-2(0x11111110)再进行右移3位得a=-1
011712EE and edx,7
011712F1 add eax,edx
011712F3 sar eax,3
011712F6 push eax
011712F7 mov dword ptr [a],eax
011712FA call dword ptr ds:[1173024h]
//变量除以常量(常量值为负的2的幂)
a = a / -2;
//和常量值为正的2的幂情况相似。先进行a除以正的2的幂,最后将结果neg取反。
//例如:a=5时,5/-2=-2;首先a/2得2,取反后a=-2;
// a=-5时,-5/-2=2;首先a/2得-2,取反后a=2;
01171300 mov eax,dword ptr [a]
cout << a;
01171303 mov ecx,dword ptr ds:[117303Ch]
01171309 cdq
0117130A sub eax,edx
0117130C sar eax,1
cout << a;
0117130E neg eax
01171310 push eax
01171311 mov dword ptr [a],eax
01171314 call dword ptr ds:[1173024h]
a = a / -8;
0117131A mov eax,dword ptr [a]
cout << a;
0117131D mov ecx,dword ptr ds:[117303Ch]
01171323 cdq
01171324 and edx,7
01171327 add eax,edx
01171329 sar eax,3
0117132C neg eax
0117132E push eax
0117132F mov dword ptr [a],eax
01171332 call dword ptr ds:[1173024h]
//变量除以常量(常量值为非2的幂)
a = a / 15;
cout << a;
//由于除法指令的周期比乘法指令周期长很多,所以编译器会用周期较短的乘法和其他指令代替除法。
//设被除数变量为a,b为除数常量,则:
// =a*=a*=a**,由于b为常量,且的取值由编译器选择,所以的//值在编译期间就可以计算出来。在vc中,n的取值都大于等于32,这样就可//以直接调整使用乘法结果的高位。这个值常被称为魔数或幻数。
01171338 mov ecx,dword ptr ds:[117303Ch]
0117133E mov eax,88888889h //魔数
01171343 imul dword ptr [a]
01171346 add edx,dword ptr [a]
01171349 sar edx,3
0117134C mov eax,edx
0117134E shr eax,1Fh
01171351 add eax,edx
01171353 push eax
01171354 mov dword ptr [a],eax
01171357 call dword ptr ds:[1173024h]
//
a = a / 11;
cout << a;
0117135D mov ecx,dword ptr ds:[117303Ch]
01171363 mov eax,2E8BA2E9h //魔数
01171368 imul dword ptr [a]
0117136B sar edx,1
0117136D mov eax,edx
0117136F shr eax,1Fh
01171372 add eax,edx
01171374 push eax
01171375 mov dword ptr [a],eax
01171378 call dword ptr ds:[1173024h]
//变量除以常量(常量值为负的非2的幂)
a = a / -15;
cout << a;
0099137E mov ecx,dword ptr ds:[99303Ch]
00991384 mov eax,77777777h
00991389 imul dword ptr [a]
0099138C sub edx,dword ptr [a]
0099138F sar edx,3
00991392 mov eax,edx
00991394 shr eax,1Fh
00991397 add eax,edx
00991399 push eax
0099139A mov dword ptr [a],eax
0099139D call dword ptr ds:[993024h]
//
a = a / -11;
009913A3 mov eax,0D1745D17h
009913A8 imul dword ptr [a]
009913AB sar edx,1
009913AD mov eax,edx
009913AF shr eax,1Fh
cout << a;
009913B2 mov ecx,dword ptr ds:[99303Ch]
009913B8 add eax,edx
009913BA push eax
009913BB mov dword ptr [a],eax
009913BE call dword ptr ds:[993024h]
//常量除以常量
a = 18 / 15;
cout << a;
//编辑期间直接计算出结果
01171393 mov ecx,dword ptr ds:[117303Ch]
01171399 push 1
0117139B mov dword ptr [a],1
011713A2 call dword ptr ds:[1173024h]
//变量除以变量
a = a / b;
//变量a和b的值不确定,所以编译期间不能进行优化,而直接使用idiv除法指令
011713A8 mov eax,dword ptr [a]
011713AB cdq
011713AC idiv eax,dword ptr [b]
cout << a;
011713AF mov ecx,dword ptr ds:[117303Ch]
011713B5 mov dword ptr [a],eax
011713B8 push eax
011713B9 call dword ptr ds:[1173024h]
system("pause");
011713BF push 117319Ch
011713C4 call dword ptr ds:[11730C4h]
return 0;
}
011713CA mov ecx,dword ptr [ebp-4]
return 0;
}
...............略
除数为非2的幂总结:
mov eax,魔数
imul ......
sar edx,......
mov reg,edx
shr reg,1fh
add edx,reg
;此后直接使用edx的值,eax弃而不用
当遇到左边指令序列时,基本可以判定是除法优化后的代码,其除法原型为变量a除以常量b,imul可表明是有符号计算,其操作数是优化前的被除数a。接下来统计右移的总次数以确定公式中的n值i,然后使用公式d=,将魔数代入公式求解常量除数b的近似值,四舍五入取整后,即可恢复除法原型。
mov eax,魔数
;这里的reg表示通用寄存器
mul reg
sub reg,edx
shr reg,1
add reg,edx
shr reg,A;这句或许没有,如果没有,则n值为1,否则这里的A就是n-1的值。
;此后直接使用reg的值,eax弃而不用。
如果遇到左边的指令序列,基本可以判定是除法优化后的代码。其除法原型为a除以常量b,mul表明是无符号计算,其操作数是优化前的被除数a,接下来统计右移的总次数以确定公式中的n值,然后使用公式b=将魔数代入公式求解常量除数b,即可恢复除法原型。
mov eax,魔数(大于7FFFFFFFH)
imul reg
add edx,reg
sar edx,......
mov reg, edx
shr reg,1fh
add edx,reg
;此后直接使用edx的值。
当遇到左边的指令序列时,基本可以断定是除法优化后的代码,其除法原型为a除以常量b,imul表明是有符号计算,其操作数是优化前的被除数a,接下来统计右移的总次数以确定公式中n的值,然后使用公式b=,将魔数代入公式求解常量除数b,即可恢复除法原型。
除数为负的非2的幂总结
mov eax,魔数(大于7FFFFFFFH)
imul reg
sar edx,......
mov reg,edx
shr reg,1FH
add edx,reg
;此后直接使用edx的值
如果遇到左边的指令序列,基本可以判定是除法优化后的代码,其除法原型为a除以常量b,imul可表明是有符号计算,其操作数是优化前的被除数a,由于魔数取值小于等于7FFFFFFFH,而imul和sar之间有sub指令来调整乘积,故可认定除数为负,且魔数为补码形式。接下来统计右移的总次数以确定公式中n的值,然后使用公式|b|=,将魔数代入公式求解常量除数|b|,即可恢复除法原型。
mov eax,魔数(小于等于7FFFFFFFH)
imul reg
sub edx,reg
sar edx,......
mov reg,edx
shr reg,1FH
add edx,reg
;此后直接使用edx的值
如果遇到左边的指令序列,基本可以判定是除法优化后的代码,其除法原型为a除以常量b,imul可表明是有符号计算,其操作数是优化前的被除数a,由于魔数取值大于7FFFFFFFH,而imul和sar之间未见任何调整代码,故可认定除数为负,且魔数为补码形式。接下来统计右移的总次数以确定公式中n的值,然后使用公式|b|=,将魔数代入公式求解常量除数|b|,即可恢复除法原型。