RE03-除法优化

除法计算约定

除法运算对应的汇编指令分为有符号idiv和无符号div两种。

image.png
所谓对x向下取整,就是取得在-∞方向最接近x的整数值,就是取得不大于x的最大整数 。

a. 向下取整

例如,+3.5向下取整得到3,-3.5向下取整得到-4
image.png

image.png

image.png

b. 向上取整
就是取得不小于x的最小整数
例如,+3.5向上取整得到4;-3.5向上取整得到-3。
image.png
image.png

c. 向零取整

所谓对x向零取整,就是取得往0方向最接近x的整数值,就是放弃小数部分。
例如,+3.5向零取整得到3,-3.5向零取整得到-3
正数就是向下取整,负数就是向上取整
image.png
image.png

移位向下取整

image.png

除法相关的数学性质

五个性质:

  1. | r | < | b |
  2. a = bq + r
  3. b = (a - r ) / q
  4. q = (a - r ) / b
  5. r = a - qb
int main(int argc, char* argv[])
{
	
	/*
	a:被除数
	b:除数
	q:商
	r:余数
	余数 等于 被除数 减去 除数乘以商
	*/
	// a / b = q
	// r = a - bq

	printf("%d\r\n",10%3);  // 1
	printf("%d\r\n",-10%3); //r = -10 - -3*3 = -1
	printf("%d\r\n",10%-3); //r = 10 - -3*-3 =  1
	printf("%d\r\n",-10%-3);//r = -10 - -3*3 = -1

	return 0;
}

相关定理和推导

定理1:

image.png

image.png

定理2:
image.png
image.png

定理3:

image.png
image.png
image.png

定理4:

image.png
image.png
image.png

image.png
image.png
image.png
image.png
image.png
image.png
image.png
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dVAV1EMC-1684413297180)(https://cdn.nlark.com/yuque/0/2023/png/27860795/1684073049212-15f34a8b-829d-457a-a317-dd0b338e160b.png#averageHue=%23fdfdfc&clientId=u0156e573-1819-4&from=paste&height=102&id=uad8d2a57&originHeight=140&originWidth=557&originalType=binary&ratio=1.375&rotation=0&showTitle=false&size=13783&status=done&style=none&taskId=u265bf9b7-34d0-4986-878b-d4e69aab1b5&title=&width=405.09090909090907)]

image.png
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mEZdd8ec-1684413297181)(https://cdn.nlark.com/yuque/0/2023/png/27860795/1684073940558-6daab52d-8cd1-45c8-817e-4a1292c5903e.png#averageHue=%23fefefe&clientId=u0156e573-1819-4&from=paste&height=274&id=u3f0910bd&originHeight=377&originWidth=1224&originalType=binary&ratio=1.375&rotation=0&showTitle=false&size=91404&status=done&style=none&taskId=ud54f5983-39d4-431d-958e-80e89f3040d&title=&width=890.1818181818181)]
image.png

image.pngimage.png

一、除法优化概念


  • 如果除数是变量,则只能使用除法指令。如果除数为常量,就有了优化的余地。
  • 数学中的除法是先除后取整,而C语言中是先取整后除(向0取整)。
  • 在C语言中:
    • 两个无符号整数相除,结果依然是无符号的;
    • 两个有符号整数相除,结果则是有符号的;
    • 有无符号混乘 ,结果是有符号,用imul
    • 如果有符号数和无符号数混除,其结果则是无符号的
      • 有符号数的最高位(符号位)被作为数据位对待,然后作为无符号数参与计算。

二、编译器优化–除数为整型常量的除法


#include <stdio.h>

int main(int argc, char* argv[])
{
	printf("%d\r\n",10 / 8);	//	常量折叠
	printf("%d\r\n",10 /argc);	//	除数为变量时无法优化,根据除数类型选择div或idiv
	printf("%d\r\n",argc /8);	//	除数为常量可以优化

	return 0;
}

image.png
:::tips
移位是向下取整的
在C语言中是向零取整的,也就是说正数是向下取整 负数是向上取整
那么只有正数符合移位的向下取整,而负数要下整转上整
根据推导7可得
:::
这是有分支的做法

int main(int argc, char* argv[])
{
	for(int i = 50;i >= -50;i--)
	{
		printf("%d / 8 = %d\r\n",i,i/8);

		//移位是向下取整的
		//在C语言中是向零取整的,也就是说正数是向下取整 负数是向上取整
		//那么只有正数符合移位的向下取整,而负数要下整转上整
		//根据推导7可得 
		if(i<0)
		{
			printf("%d >> 3 = %d\r\n\r\n",i,(i+8-1)>>3);
		//	printf("%d >> 3 = %d\r\n\r\n",i,i>>3);
		}
		else
		{
			printf("%d >> 3 = %d\r\n\r\n",i,i>>3);
		}
		

	}
	return 0;
}

无分支做法


mov eax, i
cdq			;if i >= 0,then edx = 0,else edx = 0xffffffff
and edx, 7	;if i >= 0,then edx = 0,else edx = 7
add eax, edx;if i >= 0,eax = eax + 0 = eax,else eax = eax + 7
sar eax, 3
	

:::tips
cdq : 就是将eax的最高位填充到edx
0000 0000
and:
0000 0111
= 0000 0000

1111 1111
and:
0000 0111
= 0000 0111
:::

除数为有符号数字2的幂

int main( int argc, char* argv[])
{
	printf("%d\r\n",argc / 8);
	return 0;
}

image.png

代码定式
mov eax, A 
cdq 					;符号位进到edx,A>=0,edx=0; A<0,edx=1 
and edx, 2的n次方 ‐ 1 	  ;A>0,edx=0;A<0,edx=7 
add eax, edx
sar eax, n 				;sar逻辑右移

除数为有符号数字 2

int main( int argc, char* argv[])
{
	/*
		
		  mov eax,argc
		  cdq			;if argc >= 0, then edx = 0,else edx = -1
		  sub eax,edx
		  sar eax,1
	*/
	printf("%d\r\n",argc / 2);
	return 0;
}

image.png

代码定式
mov eax,dword ptr [ebp+8]
cdq		//符号位进到edx,A>=0,edx=0; A<0,edx=‐1 
sub eax,edx
sar eax,1
push eax

除数为有符号数字-2

int main( int argc, char* argv[])
{
	/*
		
		  mov eax,argc
		  cdq			;if argc >= 0, then edx = 0,else edx = -1
		  sub eax,edx
		  sar eax,1
	*/
	printf("%d\r\n",argc / -2);
	return 0;
}

image.png

除数为无符号数字2的幂

int main( unsigned int argc, char* argv[])
{

	printf("%d\r\n",argc / 8);
	return 0;
}

直接移位
image.png
遇到shr直接移位的就是除以2的幂

除数为有符号数字2的幂的负数

int main(int argc, char* argv[])
{

	printf("%d\r\n",argc / -8);
	return 0;
}

根据向零取整的性质,可以将负号提到外面去

int main(int argc, char* argv[])
{

	printf("%d\r\n",-(argc / 8));
	return 0;
}

IDA查看
image.png

除数为无符号数字2的幂的负数

直接就是div


int main(unsigned argc, char* argv[])
{
	
	printf("%d\r\n", argc / -2);
	return 0;
}

image.png


int main(unsigned argc, char* argv[])
{
	
	printf("%d\r\n", argc / -8);
	return 0;
}

image.png

除数为无符号非2的幂

int main(unsigned int argc, char* argv[])
{
	printf("%d\r\n",argc /11);
	
	//mul指令会将结果分别存放在edx和eax寄存器中,其中edx存放高32位,eax存放低32位。
	//mul是无符号乘法imul是带符号乘法指令
	system("pause");
	return 0;
}
VC6Debug版本无优化

image.png

VC6 Release版本

image.png

解析:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J8ZOiqT9-1684413297186)(https://cdn.nlark.com/yuque/0/2023/png/27860795/1684153514465-0523641b-e840-4081-955c-9fb2b2a73338.png#averageHue=%23fefefe&clientId=ufe87fa38-6aed-4&from=paste&height=365&id=ub0180cfe&originHeight=502&originWidth=1308&originalType=binary&ratio=1.375&rotation=0&showTitle=false&size=106882&status=done&style=none&taskId=u5f2e20b1-10b0-40ff-b672-7e7b2729667&title=&width=951.2727272727273)]

2^n 除以C(常量) 触发常量折叠
n值越大 离正确结果就越近
所以在32位系统 n值起步就是32 ,n>=32
image.png
M:MagicNumber
image.png

image.png

数学原型:
  • 除法没有分配律(A/(2+3) ≠ A/2+A/3),除法可以优化为时钟周期更短的乘法和移位运算(图解),即定式:
  • AM >> n <=> A/C = n ;C = 2^n / M 向上取整 ,使用mul ,满足 AM >> n.

![image (1).png](https://img-blog.csdnimg.cn/img_convert/f485a3853b1e8cf41af7a0d57ff3bdb4.png#clientId=u88c28083-6b40-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=PVA1n&margin=[object Object]&name=image (1).png&originHeight=487&originWidth=614&originalType=binary&ratio=1&rotation=0&showTitle=false&size=102312&status=done&style=shadow&taskId=u32de421b-20a4-4194-b3c2-d093f37d581&title=#averageHue=#faf9f8&id=uTlb9&originHeight=487&originWidth=614&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

  • 结论:
    • 1.被除数 * 常量 >> (移位值N):N从32开始(编译器起步值)C越大N跟着涨
    • 2.N值越大,结果越精确。N值越小,结果误差越大。
    • 3.根据C值确定N值,C是常量,取值范围 2^(32 - 1)。
    • **已知M(MagicNum),N(右移几位),反求C(除数)公式: **C = 2n / M(最后结果向上取整)。
代码定式:
mov eax, MagicNum //eax中存储幻数(2的n次方÷c)
mul reg/mem	//内存 or 寄存器-存储被除数,乘完之后的结果存储在edx.eax中,这步相当于完成了A*M
shr edx, N	//右移N位
随堂练习

image.png
image.png

第一题:
.text:00401001                 mov     esi, [esp+4+argc]
.text:00401005                 mov     eax, 0CCCCCCCDh ;3435973837
.text:0040100A                 mul     esi
.text:0040100C                 shr     edx, 2
.text:0040100F                 push    edx
.text:00401010                 push    offset aD       ; "%d\r\n"
.text:00401015                 call    printf
第二题:
.text:0040101A                 mov     eax, 20000003h	;536870915
.text:0040101F                 mul     esi
.text:00401021                 shr     edx, 1Dh		;61
.text:00401024                 push    edx
.text:00401025                 push    offset aD       ; "%d\r\n"
.text:0040102A                 call    printf

第一题:
image.png
第一题,向上取整:5
第二题:
image.png
向上取整:4294967273
转16进制
image.png
点击DWORD,看10进制23,就是-23
image.png

int main(unsigned int argc, char* argv[])
{
	printf("%d\r\n",argc /-23);

	return 0;
}

除数为有符号非2的幂

int main(int argc, char* argv[])
{
	printf("argc:%d\r\n",argc/9);

	return 0;
}
Debug:

image.png

Release:

image.png

2^33/954437177 =8,589,934,592 / 954437177
image.png
image.png
因为移位是向下取整的 ,如果是负数那么就是向上取整的,我们就要下整转上整
image.png
所以移位的结果加上1就行,刚好加上符号位(负数的符号位是1)。
:::tips
mov eax,edx
shr eax,31 ;eax的最高位是0
add edx,eax
:::
:::tips
mov eax,edx
cdq
sub eax,edx
cdq执行时:
如果 eax是正数 那么edx是0,如果是负数那就是edx是 -1
:::
cdq:该指令先把edx的每一位置成eax的最高位

image.png

代码定式:
mov eax, MagicNumber 
imul A 				;A是指定操作数,被除数部分
sar edx, n 			;edx 右移n位
//========= 拆成两部分看,上半部分,一眼就能看出是带符号的运算(imul,sar),有公式 当x不为整数的时候【x下整 + 1 = x上整】
mov reg, edx 		
shr reg, 31		//得到符号位 eax中的值,正数是0、负数是1
add edx, reg	//正数加0,其值不变||负数加1,下整转上整
//PS:*****************
MOV EAX,EDX
CDQ
SUB EAX,EDX
//下半部分还可以用这三行代替,等价
随堂练习

image.png

0x55555556h = 1,431,655,766
1Fh = 31
image.png
2^32 / 1,431,655,766 = 3

int main(int argc,char* argv[])
{
    printf("argc: %u", argc / 3);
    return 0;
}

除数为无符号7

int main(unsigned int argc, char* argv[]) 
{ 
    printf("%d\r\n",argc / 7); 
    return 0; 
}

image.png
:::tips
.text:00401000 mov ecx, [esp+argc]
.text:00401004 mov eax, 613566757
.text:00401009 mul ecx;
.text:0040100B sub ecx, edx
image.png
.text:0040100D shr ecx, 1
.text:0040100F add ecx, edx
.text:00401011 shr ecx, 2
image.png
image.png
image.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yWRjPyJU-1684413298363)(null#card=math&code=M= \frac{2^n}{C} ,n>=32&id=D82XY)]
所以(2^32 +C),edx存不下,会出现进位
例:0x1000 0000 + 0x38e38e39
=> 0x138e38e39
所以微软工作人员是从结论出发往式子前推 直到edx存放的下
image.png
:::

VC6 Debug版本:

image.png

VC6 Release版本:

image.png
结论:乘减移加移,M最高位加1
求法:
124924925h 转 十进制
image.png
(2^32+3)/4908534053
image.png

数学推导:

![image.png](https://img-blog.csdnimg.cn/img_convert/dab3d2c74dda27b1c2e061ae0fe8e0d7.png#clientId=ufca8b31f-f344-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=673&id=u78f588dc&margin=[object Object]&name=image.png&originHeight=673&originWidth=638&originalType=binary&ratio=1&rotation=0&showTitle=false&size=107783&status=done&style=shadow&taskId=ua6e7ad32-a797-4dab-869d-8a7b3c0987b&title=&width=638#averageHue=#fafaf9&id=XV8HX&originHeight=673&originWidth=638&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qK5SLGCR-1684413297192)(https://cdn.nlark.com/yuque/0/2022/png/25515810/1657183092243-ea302317-e19e-47d2-95b8-a9bbc83265b5.png#clientId=ufca8b31f-f344-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=669&id=u40675604&margin=%5Bobject%20Object%5D&name=image.png&originHeight=669&originWidth=542&originalType=binary&ratio=1&rotation=0&showTitle=false&size=72465&status=done&style=shadow&taskId=u7700d769-52d9-43c2-b2ee-1406c2ed7c7&title=&width=542#averageHue=%23fafafa&id=s0I1u&originHeight=669&originWidth=542&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)]![image.png](https://img-blog.csdnimg.cn/img_convert/218d812f51121ee20129fd7e5f9157f3.png#clientId=ufca8b31f-f344-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=728&id=u05ccad58&margin=[object Object]&name=image.png&originHeight=728&originWidth=555&originalType=binary&ratio=1&rotation=0&showTitle=false&size=126789&status=done&style=shadow&taskId=u2072e95b-0af4-4ae3-a3a3-d7d9902d95b&title=&width=555#averageHue=#faf9f8&id=BmmDB&originHeight=728&originWidth=555&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

  • 2n / (232 + M) = C
代码定式:
mov eax MegicNnumber
mul A				; imul * 指定操作数
sub A, edx			; 指定操作数  - edx
shr A, 1			; shr 指定操作数, 1
add A, edx			; 指定操作数  + edx
shr A, n			; shr 指定操作数, n
  • 对于此代码定式:
    • (1)MegicNumber高位补1个1;
    • (2)移的总位数 + n32;
    • (3)确定指数。
    • 乘减移加移,Magic高位加1
    • 上例24924925h:高位加1 ‐>124924925h。
    • ![image.png](https://img-blog.csdnimg.cn/img_convert/0e21dfaba6233f8b8406cd0334cdea9c.png#clientId=ufca8b31f-f344-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=208&id=u0575d12e&margin=[object Object]&name=image.png&originHeight=273&originWidth=785&originalType=binary&ratio=1&rotation=0&showTitle=false&size=103626&status=done&style=shadow&taskId=ue753dce9-a50c-4091-8518-bc3206994e6&title=&width=599#averageHue=#fdf8f7&id=RizpG&originHeight=273&originWidth=785&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
    • 指数 = 32+2+1 = 35
    • ![image.png](https://img-blog.csdnimg.cn/img_convert/c112cdbd814a79c67a8e93eaaf9e8fbf.png#clientId=ufca8b31f-f344-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=164&id=ue92a8a4c&margin=[object Object]&name=image.png&originHeight=322&originWidth=1171&originalType=binary&ratio=1&rotation=0&showTitle=false&size=132763&status=done&style=shadow&taskId=u4975743a-54d9-496a-9d86-246a139a3d1&title=&width=597#averageHue=#e5e2e2&id=dxhEB&originHeight=322&originWidth=1171&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

除数为有符号7

- 对于超出megicNumber4字节表达范围的特殊值,比如7的倍数
int main(int argc) 
{ 
    printf("%d\r\n", argc / 7); 
    return 0; 
}
Debug:

image.png

Release:image.png

2^34 /2454267027 =
image.png

  • 如上例所示,除法的情况处理起来很复杂。在代码起始处出现了一个超大数值:0x92492493。这个数值是从哪里来的呢?由于除法指令的周期比乘法指令周期长很多,因此编译器会用周期较短的乘法和其他指令代替除法。数学证明
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AikOabee-1684413297194)(https://cdn.nlark.com/yuque/0/2021/png/1773393/1639662566562-6666fb2c-4878-4a19-8b82-83538b5fcc81.png#clientId=u3131d515-f364-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=111&id=dMe6x&margin=%5Bobject%20Object%5D&name=image.png&originHeight=70&originWidth=434&originalType=binary&ratio=1&rotation=0&showTitle=false&size=12427&status=done&style=shadow&taskId=uf7f40c26-43cd-4066-a577-8643950b632&title=&width=691#averageHue=%23f9f9f9&id=fPWbs&originHeight=70&originWidth=434&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)]
  • 在乘法指令中,由于edx存放乘积数据的高4字节,因此直接使用edx就等价于乘积右移了32位,再算上.text:0040100B sar edx,1,那就一共移动了33位。在地址.text:0040100D处,eax得到了edx的值,然后对eax右移了1Fh位,对应10进制也就是右移了31位,然后有个很奇怪的加法。其实这里移位的目的是得到有符号数的符号位,如果结果是正数,add edx, eax就是加0,等于什么都没干;如果结果是负数,由于其后面的代码直接使用edx作为计算结果,需要对除法的商调整加1。

![lQLPJxZ5B6SYS9HNAtDNBQCwBGt_c5Zupg0Cx1yTDsCiAA_1280_720.png](https://img-blog.csdnimg.cn/img_convert/64903c65e043210180f72c527e746544.png#clientId=u36387914-a002-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=480&id=u3ea898c5&margin=[object Object]&name=lQLPJxZ5B6SYS9HNAtDNBQCwBGt_c5Zupg0Cx1yTDsCiAA_1280_720.png&originHeight=720&originWidth=1280&originalType=binary&ratio=1&rotation=0&showTitle=false&size=35497&status=done&style=shadow&taskId=u3e557263-7853-4331-9aca-79602729283&title=&width=853.3333333333334#averageHue=#fef8f8&id=dtamB&originHeight=720&originWidth=1280&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
image.png
~8086+1 = 7F7A
![image.png](https://img-blog.csdnimg.cn/img_convert/805395c4fa078c96557f5f2f38756151.png#clientId=ufca8b31f-f344-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=573&id=u5e2f5b70&margin=[object Object]&name=image.png&originHeight=573&originWidth=823&originalType=binary&ratio=1&rotation=0&showTitle=false&size=169804&status=done&style=shadow&taskId=u489cbca4-9b6f-44ab-94d5-fb26106ed4a&title=&width=823#averageHue=#fdfafa&id=M7TwP&originHeight=573&originWidth=823&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

![image.png](https://img-blog.csdnimg.cn/img_convert/d3d362ec05226a8a4417507ee02820e3.png#clientId=ufca8b31f-f344-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=444&id=u4d1cba89&margin=[object Object]&name=image.png&originHeight=444&originWidth=1809&originalType=binary&ratio=1&rotation=0&showTitle=false&size=403276&status=done&style=shadow&taskId=u7c85efe1-7b1e-4d2d-9361-d852bae0c2d&title=&width=1809#averageHue=#f0ece4&id=fxopR&originHeight=444&originWidth=1809&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)![image.png](https://img-blog.csdnimg.cn/img_convert/58f4184690ef3b86e144a77b8d56bd01.png#clientId=ufca8b31f-f344-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=459&id=u2acf95a4&margin=[object Object]&name=image.png&originHeight=459&originWidth=1701&originalType=binary&ratio=1&rotation=0&showTitle=false&size=477517&status=done&style=shadow&taskId=u82dcfe41-dc95-4db1-b962-a904b5364c1&title=&width=1701#averageHue=#edebe3&id=sWNjy&originHeight=459&originWidth=1701&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

上图解释:

  • ax,是有符号数;8086是无符号数
  • 要求结果是个有符号数

除数为无符号数字非2的幂的负数

随堂练习

image.png
0x100000000h + 0x0Bh = 0x10000000Bh = 4294967307
0x1Fh = 31 4,294,967,289
2^31+32+1 = 2^64/4294967307 = 4,294,967,286 477,218,589
FFFF FFF6 = -10

int main(int argc,char* argv[])
{
    printf("argc: %u", (unsigned)argc / -10);
    return 0;
}

除数为有符号数字非2的幂的负数

int main(int argc,char* argv[])
{
	
    printf("argc:%d\r\n", argc / -7);
    
    printf("argc:%d\r\n", argc / -11);
}

image.png

image.png

image.png
image.png
image.png
image.png
2^34/2454267027
image.png

printf("argc:%d\r\n", argc / -11);
image.png
2^33/780,903,145
image.png

推导6 -除法有效性问题

主要解释M的指数怎么算,有什么依据。
用于确定除法误差的范围 ,即 确定 n 的值
由 推导 6 可知: M / C = q…r
0<= M /(2^n *C) < | 1/C| n值32起步,当不满时是 n 值+1 在计算,知道 n 上面不等式

image.png
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yZEo8boF-1684413297197)(https://cdn.nlark.com/yuque/0/2022/png/27242010/1657339102956-1d05927a-c13c-423b-84d7-955b60fea02a.png#averageHue=%23fbfbf2&clientId=uc9f27a04-b2e2-4&from=paste&height=800&id=uffa23c4c&originHeight=750&originWidth=423&originalType=binary&ratio=1&rotation=0&showTitle=false&size=99205&status=done&style=none&taskId=ued5eb8ce-28da-4d3c-904a-b8d50fe063c&title=&width=451.2)]
image.png
image.png

除法总结

  • 调整方法:正数是加,负数是减。
  • image.png
(1)除数为变量

div / idiv ;无优化

(2)除数为常量
  1. 无符号除法:
    1. 除数为2的幂
      1. 公式:c = 2^n
      2. 定式:

mov reg, A
shr reg , n ;直接移位

  1. 除数为非2的幂
    1. MagicNumber无进位:
      1. 公式:c = 2^(n+32) / MagicNumber
      2. **定式:**基于此原型有四种变化。

mov eax, MagicNumber
**mul ** A
**shr ** edx, n

  2. **MagicNumber有进位:**
     1. **公式:**`c = 2^(1+n+32) / (MagicNumber + 2^32)`
     2. **定式:**

**mul / sub / shr / add / shr **

mov eax, MagicNumber
mul A
sub A, edx
shr A, 1
add A, edx
shr A, n

  1. 有符号除法 :
    1. 除数为正
      1. 除数为2的幂
        1. 公式:c = 2^n
        2. 定式:

cdq / and / add / sar
mov eax, A
cdq
and edx, 2^n ‐ 1
add eax, edx
sar eax, n

  2. **除数为非2的幂**
     1. **MagicNumber为正数:**
        1. **公式:**`c = 2^(n+32) / MagicNumber`
        2. **定式:**

mov eax, MagicNumber
imul A
sar edx, n

;调整取整方向(序列不唯一)
mov reg, edx ; 或:mov eax, edx
shr reg, 31 ; 或:cdq
add reg, edx ; 或:sub eax, edx

     2. **MagicNumber为负数:**
        1. **公式:**`c = 2^(n+32) / MagicNumber`
        2. **定式:**

mov eax, MagicNumber
imul A
add edx, A
sar edx, n

mov reg, edx
shr reg, 31
add edx, reg

  1. 除数为负
    1. 除数为2的幂
      1. 公式:c = ‐(2^n)
      2. 定式:

cdq / and / add / sar / neg
mov eax, A
cdq
and edx, 2^n ‐ 1
add eax, edx
sar eax, n
neg eax

  2. **除数为非2的幂**
     1. **MagicNumber为正数:**
        1. **公式:**`c = ‐(2^(n+32) / (2^32 ‐ MagicNumber))`
        2. **定式:**

mov eax, MagicNumber
imul A
sub edx, A
sar edx, n

mov reg, edx
shr reg, 31
add edx, reg

     2. **MagicNumber为负数:**
        1. **公式:**`c = ‐(2^(n+32) / neg(MagicNumber))`
        2. **定式:**

mov eax, MagicNumber
imul A
sar edx, n

mov reg, edx
shr reg, 31
add reg, edx ; ×add edx, reg

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑桃鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值