取余大概是编程中最长使用的一种运算了,在PASCAL里面有一个关键字MOD,在C里面更简单的有一个运算符%。这个运算可以带来很多方便,稍微罗列一下:
1、在得到一个小于某数的整数
2、判定一个整数是否能被某数整除的时候
3、上一则有一个特殊的用法:判断奇偶性
4、在位BMP文件格式操作的时候,还需要计算是否需要补足位,也可能用到取余运算
这还是随便一想,当然还有很多很多别的地方也会用到了。既然这个运算如此的普遍,那我们就要考虑一下他的效率了。在前两天看CSDN的时候,有人问:n为整数时(n%10)/100与(n/100)%10谁的效率更高?其实看一眼就知道,这个命题毫无意义,因为这两个运算本身就是不等价的,不会有人因为效率问题而用其中一个运算替换另外一个。但再看一步这个问题就涉及到了取余运算在计算机里面是怎么做的这个问题了。
稍微的写一点代码,看看MS是怎么把他变成汇编的:
| 1: #include <stdio.h> 2: 3: int main(int argc, char** argv) 4: { 0040D3F0 push ebp 0040D3F1 mov ebp,esp 0040D3F3 sub esp,4Ch 0040D3F6 push ebx 0040D3F7 push esi 0040D3F8 push edi 0040D3F9 lea edi,[ebp-4Ch] 0040D3FC mov ecx,13h 0040D401 mov eax,0CCCCCCCCh 0040D406 rep stos dword ptr [edi] 5: int n = 12345; 0040D408 mov dword ptr [ebp-4],3039h 6: int Result1 = 0; 0040D40F mov dword ptr [ebp-8],0 7: int Result2 = 0; 0040D416 mov dword ptr [ebp-0Ch],0 8: 9: Result1 = ( n % 10 ) / 100; 0040D41D mov eax,dword ptr [ebp-4] 0040D420 cdq 0040D421 mov ecx,0Ah 0040D426 idiv eax,ecx 0040D428 mov eax,edx 0040D42A cdq 0040D42B mov ecx,64h 0040D430 idiv eax,ecx 0040D432 mov dword ptr [ebp-8],eax 10: 11: Result2 = ( n / 100 ) % 10; 0040D435 mov eax,dword ptr [ebp-4] 0040D438 cdq 0040D439 mov ecx,64h 0040D43E idiv eax,ecx 0040D440 cdq 0040D441 mov ecx,0Ah 0040D446 idiv eax,ecx 0040D448 mov dword ptr [ebp-0Ch],edx 12: 13: return 0; 0040D44B xor eax,eax 14: } 0040D44D pop edi 0040D44E pop esi 0040D44F pop ebx 0040D450 mov esp,ebp 0040D452 pop ebp 0040D453 ret |
很明显,我们看到了汇编指令里面没有取余的指令,当然这个在学习汇编的时候,我们就知道的。所谓的取余是通过一个除法指令和一个移位指令组合完成的。我们知道idiv eax,ecx的整数结果是保存在eax中的,而经接着下面一句就是mov eax,edx,很明显寄存器edx里面保存的就是余数了。而这句mov dword ptr [ebp-0Ch],edx看的就更明显了,直接把余数放到变量的地址里面去了。
这样看到所谓的取余就是一个除法运算。做嵌入式系统时间一长,我也有了毛病了,一听说除法运算脑子里第一个反映出来的名次就是“慢”。
在社区里面有人提出一个问题,求一个正整数的个位数,要求程序的效率要比跟10求模效率高。
这个问题还在思考中,听说是某企业的面试题,呵呵,看来也是一家嵌入式的公司啊。
