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|,即可恢复除法原型。 |