今天讲讲arm汇编中除法的底层实现。汇编代码本身比较长了,如需参考请直接拉到文末。

下面我直接把arm的除法算法的汇编代码转译成C语言的代码贴出来,并进行解析。

因为篇幅有限,所以在此只解析无符号整型的除法运算,关于无符号除法和有符号除法的区别请参考上一篇推送

代码较长如下,电脑端看效果更佳,如无耐心请直接拉下去看讲解即可:

#include<stdio.h>

unsigned int count_leading_zeros(unsigned int num)
{
unsigned int cnt = 0;
while(!(num & 0x80000000) && cnt < 32){
cnt++;
num <<= 1;
}
return cnt;
} unsigned int div_unsigned(unsigned int dividend, unsigned int divisor)
{
unsigned int answer = 0;
int cc;
unsigned int divisor_lz = 0, dividend_lz = 0; if (divisor == 1){
return dividend;
}else if (divisor < 1){
return -1;
} if (divisor == dividend){
return 1;
}else if (dividend < divisor){
return 0;
} if ((divisor & (divisor - 1)) == 0){
return dividend >> (31 - count_leading_zeros(divisor));
} divisor_lz = count_leading_zeros(divisor);
dividend_lz = count_leading_zeros(dividend);
printf("dividend[0x%x], dividend_lz[%d], divisor[0x%x], divisor_lz[%d]\n", dividend, dividend_lz, divisor, divisor_lz);
cc = divisor_lz - dividend_lz;
while(cc >= 0){
answer <<= 1;
if (dividend >= (divisor << cc)){
answer += 1;
dividend -= (divisor << cc);
}
cc--;
}
return answer;
}
main(){
unsigned int a = 0x80000000 / 3;
unsigned int b = div_unsigned(0x80000000, 3);
printf("[0x%x][0x%x]",a, b);
}

2次幂和移位运算

在以上代码中我们终于看到了移位运算对除法运算的优化:

当除数是2的N次幂时,可以直接对被除数做右移运算来代替除法, 比如除数是2即(2的1次幂),此时只需要对被除数做一次右移即可,同理如果除数是8则对被除数做三次右移。

而判断一个数字是不是2的N次幂只需要一行代码:

    if ((divisor & (divisor - 1)) == 0){

这一行代码也几乎就是leetcode的第231题2的幂的答案:

2^x n n - 1 n & (n - 1)
2^0 0001 0000 (0001) & (0000) == 0
2^1 0010 0001 (0010) & (0001) == 0
2^2 0100 0011 (0100) & (0011) == 0
2^3 1000 0111 (1000) & (0111) == 0

如有疑问请继续参考leetcode的题解:https://leetcode-cn.com/problems/power-of-two/solution/power-of-two-er-jin-zhi-ji-jian-by-jyd/

而计算2的N次幂中的N,也只需要这一句即可:

(31 - count_leading_zeros(divisor))

count_leading_zeros即为一个32bit的数字以二进制呈现的时候,从高位向低位数开始数有连续多少个0的数量。

比如数字2的二进制是: 0000 0000 0000 0000 0000 0000 0000 0010

在第一个bit1出现之前有30个0。

判断是否是2的N次幂,并且计算出N的大小并进行右移也只需要以下三行代码。

    if ((divisor & (divisor - 1)) == 0){
return dividend >> (31 - count_leading_zeros(divisor));
}

为什么要使用count_leading_zeros这种方法呢,虽然我在上面的代码中定义了函数count_leading_zeros,但是在arm汇编中只需要一条指令clz即可,计算2的N次幂的N加上右移也只需要三条指令即可,非常高效:

clz     r2, r1 //计算leading zeros的数量
rsb r2, r2, #31 //31 - count_leading_zeros(divisor)
lsr.w r0, r0, r2 // 进行右移

二进制的除法解析

那么更多情况下,除数也并不是2的N次幂。如果除数是3,那么还是要做一下正规的除法了。

我做了一张图来对比8/3的十进制和二进制的除法。

在二进制时,任何一个bit不可能大于1,所以当两个数字的leading zeros相同时,被除数不可能会整除除数超过或者等于两次。也就是说leading zeros相同时,被除数要么能整除除数一次,要么是0次。

二进制运算除法的时候,首先会对除数做左移操作,让除数和被除数进行“对齐”(即leading zeros数量相同),如果此时的被除数大于等于此时(左移后的)除数,那么在相应的答案位上置一,否则置0。然后对(左移后的)除数​做右移一位操作再继续和被除数做比较,直到除数恢复成原来的初始值(这时候会作最后一次运算)。如下代码所示:

    cc = divisor_lz - dividend_lz;
while(cc >= 0){
answer <<= 1;
if (dividend >= (divisor << cc)){
answer += 1;
dividend -= (divisor << cc);
}
cc--;
}

所以在二进制整型数字的除法世界中,只需要减法和移位操作就能够满足除法运算的需求。最后我才发现,二进制的除法原本就是这么简单,比十进制的除法还要简单。

本文完,以下为参考资料。

arm的指令集查文档:

http://users.ece.utexas.edu/~valvano/Volume1/QuickReferenceCard.pdf

https://iitd-plos.github.io/col718/ref/arm-instructionset.pdf

div无符号整形的除法汇编如下:

00010490 <__udivsi3>:
10490: 1e4a subs r2, r1, #1
10492: bf08 it eq
10494: 4770 bxeq lr
10496: f0c0 8124 bcc.w 106e2 <__udivsi3+0x252>
1049a: 4288 cmp r0, r1
1049c: f240 8116 bls.w 106cc <__udivsi3+0x23c>
104a0: 4211 tst r1, r2
104a2: f000 8117 beq.w 106d4 <__udivsi3+0x244>
104a6: fab0 f380 clz r3, r0
104aa: fab1 f281 clz r2, r1
104ae: eba2 0303 sub.w r3, r2, r3
104b2: f1c3 031f rsb r3, r3, #31
104b6: a204 add r2, pc, #16 ; (adr r2, 104c8 <__udivsi3+0x38>)
104b8: eb02 1303 add.w r3, r2, r3, lsl #4
104bc: f04f 0200 mov.w r2, #0
104c0: 469f mov pc, r3
104c2: bf00 nop
104c4: f3af 8000 nop.w
104c8: ebb0 7fc1 cmp.w r0, r1, lsl #31
104cc: bf00 nop
104ce: eb42 0202 adc.w r2, r2, r2
104d2: bf28 it cs
104d4: eba0 70c1 subcs.w r0, r0, r1, lsl #31
104d8: ebb0 7f81 cmp.w r0, r1, lsl #30
104dc: bf00 nop
104de: eb42 0202 adc.w r2, r2, r2
104e2: bf28 it cs
104e4: eba0 7081 subcs.w r0, r0, r1, lsl #30
104e8: ebb0 7f41 cmp.w r0, r1, lsl #29
104ec: bf00 nop
104ee: eb42 0202 adc.w r2, r2, r2
104f2: bf28 it cs
104f4: eba0 7041 subcs.w r0, r0, r1, lsl #29
104f8: ebb0 7f01 cmp.w r0, r1, lsl #28
104fc: bf00 nop
104fe: eb42 0202 adc.w r2, r2, r2
10502: bf28 it cs
10504: eba0 7001 subcs.w r0, r0, r1, lsl #28
10508: ebb0 6fc1 cmp.w r0, r1, lsl #27
1050c: bf00 nop
1050e: eb42 0202 adc.w r2, r2, r2
10512: bf28 it cs
10514: eba0 60c1 subcs.w r0, r0, r1, lsl #27
10518: ebb0 6f81 cmp.w r0, r1, lsl #26
1051c: bf00 nop
1051e: eb42 0202 adc.w r2, r2, r2
10522: bf28 it cs
10524: eba0 6081 subcs.w r0, r0, r1, lsl #26
10528: ebb0 6f41 cmp.w r0, r1, lsl #25
1052c: bf00 nop
1052e: eb42 0202 adc.w r2, r2, r2
10532: bf28 it cs
10534: eba0 6041 subcs.w r0, r0, r1, lsl #25
10538: ebb0 6f01 cmp.w r0, r1, lsl #24
1053c: bf00 nop
1053e: eb42 0202 adc.w r2, r2, r2
10542: bf28 it cs
10544: eba0 6001 subcs.w r0, r0, r1, lsl #24
10548: ebb0 5fc1 cmp.w r0, r1, lsl #23
1054c: bf00 nop
1054e: eb42 0202 adc.w r2, r2, r2
10552: bf28 it cs
10554: eba0 50c1 subcs.w r0, r0, r1, lsl #23
10558: ebb0 5f81 cmp.w r0, r1, lsl #22
1055c: bf00 nop
1055e: eb42 0202 adc.w r2, r2, r2
10562: bf28 it cs
10564: eba0 5081 subcs.w r0, r0, r1, lsl #22
10568: ebb0 5f41 cmp.w r0, r1, lsl #21
1056c: bf00 nop
1056e: eb42 0202 adc.w r2, r2, r2
10572: bf28 it cs
10574: eba0 5041 subcs.w r0, r0, r1, lsl #21
10578: ebb0 5f01 cmp.w r0, r1, lsl #20
1057c: bf00 nop
1057e: eb42 0202 adc.w r2, r2, r2
10582: bf28 it cs
10584: eba0 5001 subcs.w r0, r0, r1, lsl #20
10588: ebb0 4fc1 cmp.w r0, r1, lsl #19
1058c: bf00 nop
1058e: eb42 0202 adc.w r2, r2, r2
10592: bf28 it cs
10594: eba0 40c1 subcs.w r0, r0, r1, lsl #19
10598: ebb0 4f81 cmp.w r0, r1, lsl #18
1059c: bf00 nop
1059e: eb42 0202 adc.w r2, r2, r2
105a2: bf28 it cs
105a4: eba0 4081 subcs.w r0, r0, r1, lsl #18
105a8: ebb0 4f41 cmp.w r0, r1, lsl #17
105ac: bf00 nop
105ae: eb42 0202 adc.w r2, r2, r2
105b2: bf28 it cs
105b4: eba0 4041 subcs.w r0, r0, r1, lsl #17
105b8: ebb0 4f01 cmp.w r0, r1, lsl #16
105bc: bf00 nop
105be: eb42 0202 adc.w r2, r2, r2
105c2: bf28 it cs
105c4: eba0 4001 subcs.w r0, r0, r1, lsl #16
105c8: ebb0 3fc1 cmp.w r0, r1, lsl #15
105cc: bf00 nop
105ce: eb42 0202 adc.w r2, r2, r2
105d2: bf28 it cs
105d4: eba0 30c1 subcs.w r0, r0, r1, lsl #15
105d8: ebb0 3f81 cmp.w r0, r1, lsl #14
105dc: bf00 nop
105de: eb42 0202 adc.w r2, r2, r2
105e2: bf28 it cs
105e4: eba0 3081 subcs.w r0, r0, r1, lsl #14
105e8: ebb0 3f41 cmp.w r0, r1, lsl #13
105ec: bf00 nop
105ee: eb42 0202 adc.w r2, r2, r2
105f2: bf28 it cs
105f4: eba0 3041 subcs.w r0, r0, r1, lsl #13
105f8: ebb0 3f01 cmp.w r0, r1, lsl #12
105fc: bf00 nop
105fe: eb42 0202 adc.w r2, r2, r2
10602: bf28 it cs
10604: eba0 3001 subcs.w r0, r0, r1, lsl #12
10608: ebb0 2fc1 cmp.w r0, r1, lsl #11
1060c: bf00 nop
1060e: eb42 0202 adc.w r2, r2, r2
10612: bf28 it cs
10614: eba0 20c1 subcs.w r0, r0, r1, lsl #11
10618: ebb0 2f81 cmp.w r0, r1, lsl #10
1061c: bf00 nop
1061e: eb42 0202 adc.w r2, r2, r2
10622: bf28 it cs
10624: eba0 2081 subcs.w r0, r0, r1, lsl #10
10628: ebb0 2f41 cmp.w r0, r1, lsl #9
1062c: bf00 nop
1062e: eb42 0202 adc.w r2, r2, r2
10632: bf28 it cs
10634: eba0 2041 subcs.w r0, r0, r1, lsl #9
10638: ebb0 2f01 cmp.w r0, r1, lsl #8
1063c: bf00 nop
1063e: eb42 0202 adc.w r2, r2, r2
10642: bf28 it cs
10644: eba0 2001 subcs.w r0, r0, r1, lsl #8
10648: ebb0 1fc1 cmp.w r0, r1, lsl #7
1064c: bf00 nop
1064e: eb42 0202 adc.w r2, r2, r2
10652: bf28 it cs
10654: eba0 10c1 subcs.w r0, r0, r1, lsl #7
10658: ebb0 1f81 cmp.w r0, r1, lsl #6
1065c: bf00 nop
1065e: eb42 0202 adc.w r2, r2, r2
10662: bf28 it cs
10664: eba0 1081 subcs.w r0, r0, r1, lsl #6
10668: ebb0 1f41 cmp.w r0, r1, lsl #5
1066c: bf00 nop
1066e: eb42 0202 adc.w r2, r2, r2
10672: bf28 it cs
10674: eba0 1041 subcs.w r0, r0, r1, lsl #5
10678: ebb0 1f01 cmp.w r0, r1, lsl #4
1067c: bf00 nop
1067e: eb42 0202 adc.w r2, r2, r2
10682: bf28 it cs
10684: eba0 1001 subcs.w r0, r0, r1, lsl #4
10688: ebb0 0fc1 cmp.w r0, r1, lsl #3
1068c: bf00 nop
1068e: eb42 0202 adc.w r2, r2, r2
10692: bf28 it cs
10694: eba0 00c1 subcs.w r0, r0, r1, lsl #3
10698: ebb0 0f81 cmp.w r0, r1, lsl #2
1069c: bf00 nop
1069e: eb42 0202 adc.w r2, r2, r2
106a2: bf28 it cs
106a4: eba0 0081 subcs.w r0, r0, r1, lsl #2
106a8: ebb0 0f41 cmp.w r0, r1, lsl #1
106ac: bf00 nop
106ae: eb42 0202 adc.w r2, r2, r2
106b2: bf28 it cs
106b4: eba0 0041 subcs.w r0, r0, r1, lsl #1
106b8: ebb0 0f01 cmp.w r0, r1
106bc: bf00 nop
106be: eb42 0202 adc.w r2, r2, r2
106c2: bf28 it cs
106c4: eba0 0001 subcs.w r0, r0, r1
106c8: 4610 mov r0, r2
106ca: 4770 bx lr
106cc: bf0c ite eq
106ce: 2001 moveq r0, #1
106d0: 2000 movne r0, #0
106d2: 4770 bx lr
106d4: fab1 f281 clz r2, r1
106d8: f1c2 021f rsb r2, r2, #31
106dc: fa20 f002 lsr.w r0, r0, r2
106e0: 4770 bx lr
106e2: b108 cbz r0, 106e8 <__udivsi3+0x258>
106e4: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff
106e8: f000 b966 b.w 109b8 <__aeabi_idiv0>

我竟在arm汇编除法算法里找到了leetcode某道题的解法的更多相关文章

  1. GNU风格 ARM汇编语法指南

    汇编源程序一般用于系统最基本的初始化:初始化堆栈指针.设置页表.操作 ARM的协处理器等.这些初始化工作完成后就可以跳转到C代码main函数中执行. 1.  GNU汇编语言语句格式 任何Linux汇编 ...

  2. ARM 汇编的一些规范

    A.5.1  文件格式        ARM 源程序文件(即源文件)为文件格式,可以使用任一文本编辑器编写程序代码.         在一个项目中,至少要有一个汇编源文件或C 程序文件,可以有多个汇编 ...

  3. ARM 汇编指令

    ARM汇编程序特点: l         所有运算处理都是发生通用寄存器(一般是R0~R14)的之中.所有存储器空间(如C语言变量的本质就是一个存储器空间上的几个BYTE).的值的处理,都是要传送到通 ...

  4. linux驱动系列之arm汇编

    在arm平台学习linux时,会遇到arm汇编指令,arm汇编指令与8086汇编指令很多地方都不同,在此记下来以免后面忘了,同时在学习了汇编指令之后分析一些汇编指令编写的代码. 一.相对跳转指令b.b ...

  5. 转载:Source Insight查看ARM汇编源程序 && 高亮显示程序 && Source Insight打开project窗口出错

    (1)Source Insight查看ARM汇编源程序.做ARM嵌入式开发时,有时得整汇编代码,但在SIS里建立PROJECT并ADD TREE的时候,根据默认设置并不会把该TREE里面所有汇编文件都 ...

  6. 经常使用ARM汇编指令

    一面学习,一面总结,一面记录. 以下是整理在网上找到的一些资料,简单整理记录一下,方便以后查阅. ARM处理器的指令集能够分为跳转指令.数据处理指令.程序状态寄存器(PSR)处理指令.载入/存储指令. ...

  7. C/C++ 中嵌入 arm 汇编

    GCC编译器支持直接在C或者C++代码中,嵌入ARM汇编代码.其基本格式非常简单,大致如下: __asm__ [__volatile__] ( assembler template : [output ...

  8. ARM 汇编与C调用的若干问题(一般函数调用情况)

    ARM 汇编与C之间的函数调用需要符合ATPCS,建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递:若形参个数大于4,大于4的部分必须通过堆栈进行传 ...

  9. ARM 汇编的mov操作立即数的疑问

    1. 因为对arm汇编有些指令还不能理解,特别是一些相似功能指令间的区别.偶然在网上搜到"faq ARM assembly",其中描述的几个问题还是值得好好研究一下. 2. 慢慢的 ...

随机推荐

  1. android开发之gridView的一些属性。(项目经验总结)

    1.android:numColumns="auto_fit"   //GridView的列数设置为自动 2.android:columnWidth="90dp &quo ...

  2. MyBatis-Plus分页——PageHelper和IPage介绍

    两个都用于分页,常用的应该是PageHelper了,理解了一下源码后发现IPage比PageHelper好用. 使用方法是 PageHelper.startPage()然后后边写sql就可以. 紧接着 ...

  3. Oracle中真正稳妥的求三甲的方法

    坐地铁回家路上忽然想起,三甲排名可能为多个,只取三名岂不荒谬.不信请看下面数据: create table tb_score( id number(4,0) primary key, name nva ...

  4. Python的链接数上升得太快了!足见Python之火!

  5. leetcode刷题-91解码方法

    题目 一条包含字母 A-Z 的消息通过以下方式进行了编码: 'A' -> 1'B' -> 2...'Z' -> 26给定一个只包含数字的非空字符串,请计算解码方法的总数. 示例 1: ...

  6. Python实现加密的RAR文件解压(密码已知)

    博主之前在网上找了很多资料,发现rarfile库不能直接调用,需要安装unrar模块,下面将详细介绍整个实现流程. 第一步:安装unrar模块,直接pip install unrar可能会找不到库,需 ...

  7. jenkins iOS自动打包

    1.Jenkins配置 采用命令行下载配置Jenkins,防止产生权限问题 1)先安装brew,打开命令行,输入:/usr/bin/ruby -e "$(curl -fsSL https:/ ...

  8. adb连接手机

    1. 通过wifi, 利用adb来连接手机. 在pc的cmd中输入命令: adb connect 192.168.1.100 其中adb就是手机的ip. 如果连接成功, 就可以进入android的sh ...

  9. 执行./install.sh时报错-bash: ./install.sh: /bin/bash^M: 坏的解释器: 没有那个文件或目录

    百度解释说是因为这个文件在windows下编辑过,windows下每一行的结尾是\n\r, 而linux下每一行结尾是\n,所以只需要删除这个文件中的\r字符就可以了sed -i 's/\r$//' ...

  10. oracle之二ASM 管理

    Oracle ASM 管理(PPT-II:602-636) 16.1 什么是ASM 自动存储管理即ASM(Automatic Storage Management),是Oracle提供的一项管理磁盘的 ...