看看编译器是怎样用乘法代替除法的

在0与1的计算机世界中,最复杂的运算就是除法了。复杂到什么程度呢?就是不到万不得已的情况下,连编译器自己都不愿意产生除法指令。

备注:以下分析,主要针对的情况为 除数不是2的指数。如果除法是2的指数,尽可以简单地使用移位来运算。比如, 20 / 4 = 20 >> 2 = 5(2 ^ '2' = 4)。

1.例程代码

简单的C程序:

// div5.c - small example for test
#include 
   
   
    
    

int div5(int num)
{
	int result;

	result = num / 5;

	return result;
}

int main()
{
	printf("50 / 5 is %d\n", div5(50));
	printf("-50 / 5 is %d\n", div5(-50));
	return 0;
}
   
   
Linux下通过  gcc -S div5.c 产生的汇编代码 div5.s

.file	"div5.c"
	.text
.globl div5
	.type	div5, @function
div5:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$16, %esp
	movl	8(%ebp), %ecx       # ECX = num
	movl	$1717986919, %edx   # EDX = 1717986919 = ((1 << 33) + 3) / 5
	movl	%ecx, %eax          # EAX = num
	imull	%edx                # EDX:EAX = num * 1717986919
	sarl	%edx                # EDX:EAX >> 33 equql to EDX >> 1
	movl	%ecx, %eax
	sarl	$31, %eax           # retrieve sign bit of num
	movl	%edx, %ecx
	subl	%eax, %ecx          # adjust negtive result 
	movl	%ecx, %eax
	movl	%eax, -4(%ebp)
	movl	-4(%ebp), %eax
	leave
	ret
	.size	div5, .-div5
	.section	.rodata
.LC0:
	.string	"50 / 5 is %d\n"
.LC1:
	.string	"-50 / 5 is %d\n"
	.text
.globl main
	.type	main, @function
main:
	pushl	%ebp
	movl	%esp, %ebp
	andl	$-16, %esp
	subl	$16, %esp
	movl	$50, (%esp)
	call	div5
	movl	$.LC0, %edx
	movl	%eax, 4(%esp)
	movl	%edx, (%esp)
	call	printf
	movl	$-50, (%esp)
	call	div5
	movl	$.LC1, %edx
	movl	%eax, 4(%esp)
	movl	%edx, (%esp)
	call	printf
	movl	$0, %eax
	leave
	ret
	.size	main, .-main
	.ident	"GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"
	.section	.note.GNU-stack,"",@progbits

2.代码分析

通过代码分析,这里将除法转换为乘法的方法就是

x = y / divider = (y * M) >> 33;  其中M = ((1 << 33) + 3)  / divider,当divider=5, M=1717986919=0x66666667

推导证明过程,我没有深入研究,在此也不作赘述。

汇编代码部分,我就乘法指令“IMUL source”,作简单说明。

source为乘数,在使用IMUL指令之前,需将被乘数置于EAX寄存器中,如果结果为64位值(如两个4字节数之间的乘法),最终的结果放置在EDX:EAX寄存器对中,高32位值在EDX中,低32位值在EAX中。将(y * divider)的结果右移33位,其实就是将EDX右移1位。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值