再来看一个例子,巩固一下。假设有以下3行反汇编代码,现在来反推回高级代码。
3行代码合起来即:(R0 * 0xCCCCCCCD) >> 34。
除法优化原理:a/b = (a*c) >> n,其中c=(2^n)/b。
由(R0 * 0xCCCCCCCD) >> 34,可知n=34,c=0xCCCCCCCD。根据c=(2^ n)/b,可知b=(2^ n)/c=(2^34)/0xCCCCCCCD=4.99999999971,即b=5(因为c值有一个很小的,不影响除法运算结果的误差,所以这里得到的值近似5)。所以,上述3行汇编代码对应的高级代码即:R0/5。与实际的源码正好对应的上:
再回头看一下刚开始提到的“R0 * 0xAAAAAAAB >> 32”,这个对应的高级代码应该是什么?
除法优化原理:a/b = (a*c) >> n,其中c=(2^n)/b。
由“(R0 * 0xAAAAAAAB) >> 32”,可知n=32,c=0xAAAAAAAB。根据c=(2^ n)/b,可知b=(2^ n)/c=(2^32)/0xAAAAAAAB=1.49999999983,即b=1.5。所以“(R0 * 0xAAAAAAAB) >> 32”即R0/1.5。不过,这里提到的除法优化是针对整数常量来说的,所以实际就是R0/(3/2),即R0*2/3。
现在把test函数简单修改一下:
原先参数类型是unsigned int,现在参数类型是int。看一下a/3对应的反汇编代码:
这4行代码合起来就是:(R0 * 0x55555556) >> 32 – (R0 >> 31),其中R0 >> 31是算数右移。先忽略后面的减法,只关心“(R0*0x55555556)>>32”。
除法优化原理:a/b = (a*c) >> n,其中c=(2^n)/b。
由“(R0 * 0x55555556) >> 32”,可知n=32,c=0x55555556。根据c=(2^ n)/b,可知b=(2^ n)/c=(2^32)/0x55555556=2.9999999986,即b=3。所以“(R0 * 0x55555556) >> 32”即R0/3。这么一看,貌似后面的“– (R0 >> 31)”是多余的。其实不然,简单分析一下。
参数类型是int,“R0 >> 31”就是取符号位(算数右移)。那么有两种情况:
1)R0是正数,那么R0 >> 31结果为0,减法相当于什么也没做。
除法优化原理还是:a/b = (a*c) >> n,其中c=(2^n)/b。
2)R0是负数,那么R0 >> 31结果为0xFFFFFFFF,即-1,减-1相当于加1。
除法优化原理变成:a/b =( (a*c) >> n) + 1,其中c=(2^n)/b。
为什么被除数为负数时,后面要加1呢?因为“(a*c) >> n”是向下取整的结果。加1是为了向0取整,而c/c++语言对于整数除法的规定正是向0取整。