欢迎光临散文网 会员登陆 & 注册

从汇编代码中探究整数的除法运算

2021-04-28 11:01 作者:刹那-Ksana-  | 我要投稿

前言

看过我的汇编语言101的人应该都知道,指令div D或者idiv D是用于做除法的指令(具体的过程可以参考我的汇编语言101视频)

例如:

是 n/5 的指令,运算结果会储存在eax中。

但是,以上的Assembly最多只会在-O0见到,如果把优化等级调到最高的-O3,你基本上见不到div这个指令。

此外,我对电路设计不是很了解,如果你想要知道CPU是如何做除法运算的,可以参考这里:https://electronics.stackexchange.com/questions/22410/how-does-division-occur-in-our-computers

*注意:本文之后所有数字,如果在末尾加b,则代表这是一个2进制下的数,否则就是十进制下的数

一、n/1

我们先从最简单的n/1开始,不用说,n/1那就是n,如果是-O0,编译器会生产如下的汇编代码

这就是没有优化前的代码,我们说什么,编译器做什么。如果是-O3的话,编译器会直接返回n,即,直接忽略我们的除法运算。

本文之后的内容都以-O3为标准,不在谈及-O0

二、整数除以2的n次方

接下来是非负整数除以2,众所周知,我们的计算机用bit shift做整数的除法运算,比如说

这个正数的除法运算对应的assembly:

把所有bit往右移动1的距离,比如1011b移动后就变成了0101b,十进制下的11就变成了5。

同样的,如果我们要除以4,那么我们就把eax朝右移动2位,除以8就移动3位。

但是注意,如果我们bit位移量大于我们数据类型的长度的话,会出现什么呢?比如说,让一个word类型的数据除以2的17次方,这个时候,我们的程序不会再进行bit位移,而是直接返回0

xor自己本身就是将自己清零)

但是等等,这个并不是故事的全部,上面的是非负整数,如果是整数的话,我们的代码还会再多2行

我们在这里做得运算,用公式写出来就是

(n%2BnSHR31)SAR1

引入n SHR 31的作用显而易见,就是n为负数的情况下——比如n=-2时,bit右移31位后会获得1b,然后二者相加得到-1,然后做bit右移1位同时sign填充后获得-1;如果是在n为正数的情况下,n SHR 31这个数直接被忽略掉了。

三、整数除以整数

接下来我们来看一下整数与整数的除法运算,我们先假定我们的两个数都是非负数,如一个非负整数n除以5,将会得到如下的汇编代码:

我们还是没有看见div的身影。上面的代码稍微有点复杂,但是我们本质上就是,将n的值乘以3435973837,然后bit右移34位(注意此处rax长度为64)

我们在这里可以随便找一个数试一试,比如n=123:

123*3435973837%3D422624781951

1100010 01100110 01100110 01100110 01111111b

右移后我们得到11000b,即24;而123/5的结果则是24.6,去掉小数点我们得到24

我们试着把3435973837这个数字去除以2^34,我们会得到非常接近0.2的数,准确的说是0.2后面很多个0然后一个1。

我们现在取消我们非负数的限制,同样的n/5我们会得到以下的代码

因为在这里我们涉及到正负号,所以我们有movsxsar两个和符号有关的指令,并且最后一步我们居然是做一个减法运算。用公式表达就是:

(%20n*1717986919%20SAR33%20)%20-%20(%20n%20SAR%2031%20)%20%20

我们可以理解为n*0.2然后减去一个非常小的数 n SAR 31;为什么是这个组合,我不知道,但是nSAR31似曾相识?我们上面那一节里面貌似也有一个n SHR 31?

以上就是3种不同情况下的除法(如果是浮点型数据的除法的话,我们不可避免地还是会用到divss这个指令)

下一篇文章我会再和大家来讲讲取余运算,剧透一下,还是没有div这个指令。

参考工具:Compiler Explorer: https://godbolt.org/

THE END.

从汇编代码中探究整数的除法运算的评论 (共 条)

分享到微博请遵守国家法律