编译器整数除法的优化

编译器整数除法的优化

整数的除法有2种情况

被除数是未知数

那么编译器会使用div指令,运行效率会下降

被除数/除数是常量

那么情况会比较复杂,如果除数 2 n 2^n 2n,就会使用shr a,n。其中a是被除数,n是2的幂。如果除数不是2呢?那么情况就会更加复杂了,不同的编译器会有不同的算法。通常的思路是将除法转换成乘法来提高运行的效率,最常见的优化算法便是倒数相乘了

a b = a ∗ ( 1 b ) \frac{a}{b}=a*(\frac{1}{b}) ba=a(b1)

下面用一个实例说明

#include<stdio.h>
int main(){
    int a;
    scanf("%d",&a);
    printf("%d",a/11);
    return 0;
}

这是一个非常简单的整数除法例子,下面我列出核心汇编代码进行讲解

mov eax,2E8BA2E9
imul ecx   //ecx是我们的a
sar edx,1
mov ecx,edx
shr ecx,1F
add edx,ecx

核心的除法优化算法其实就是短短的6行,那么这有什么可讲的呢?

首先eax是由编译器生成的一个数,然后 a ∗ e a x a*eax aeax,得到的积低32位存储在eax中,高32位存储在edx中,如果积大于32位的话。

sar edx,1

则是直接取出高32位来看,相当于直接舍去了低32位,也就相当于先右移了32位。再右移1位,相当于我们得到的积总共向右移了33位

a ∗ ( 2 E 8 B A 2 E 9 h > > 33 ) a*(2E8BA2E9h>>33) a(2E8BA2E9h>>33)等于 a ∗ ( 2 E 8 B A 2 E 9 h / 2 33 ) a*(2E8BA2E9h/2^{33}) a(2E8BA2E9h/233), ( 2 E 8 B A 2 E 9 h / 2 33 ) ≈ 0.090909 (2E8BA2E9h/2^{33})\approx0.090909 (2E8BA2E9h/233)0.090909也就是所谓的倒数了。

第5行的右移31位是为了得到结果的符号位,这里主要是看符号位是否为1,也就是是否为负数。如果是负数的话,符号位为1,最后的结果+1。

那么问题来了,为什么最后要加1呢?

这是因为负数算术右移负数除以2最后的结果其实是不同的,虽然我们常常认为右移1位就是除以2。

负数算术右移是向下取整的,换句话说 负 数 s h r 的 结 果 + 1 = 负 数 除 以 2 的 结 果 负数shr的结果+1 = 负数除以2的结果 shr+1=2。所以最后要加1。

1.png

当然这是有符号数的除法,如果是无符号数的除法,使用的汇编指令就是shr了。而无符号数永远为正,自然也不会存在这样的问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值