当我使用VC ++ 10编译此代码时:
DWORD ran = rand();
return ran / 4096;
我得到了这个反汇编:
299: {
300: DWORD ran = rand();
00403940 call dword ptr [__imp__rand (4050C0h)]
301: return ran / 4096;
00403946 shr eax,0Ch
302: }
00403949 ret
干净简洁,用一个逻辑右移的2的幂代替除法。
然而,当我编译这段代码时:
int ran = rand();
return ran / 4096;
我得到了这个反汇编:
299: {
300: int ran = rand();
00403940 call dword ptr [__imp__rand (4050C0h)]
301: return ran / 4096;
00403946 cdq
00403947 and edx,0FFFh
0040394D add eax,edx
0040394F sar eax,0Ch
302: }
00403952 ret
在进行正确的算术移位之前执行一些操作。
这些额外操作的必要性是什么?为什么算术移位不够?
答案 0 :(得分:90)
原因是2 ^ n的无符号除法可以非常简单地实现,而有符号除法则更复杂。
unsigned int u;
int v;
对于u / 4096
的所有可能值, u >> 12
相当于u
。
v / 4096
NOT 等同于v >> 12
- 它在v < 0
时分解,因为当涉及负数时,转移与分割的舍入方向不同
答案 1 :(得分:34)
“额外操作”补偿了算术右移使结果向负无穷大舍入的事实,而除法将结果舍入为零。
例如,-1 >> 1
为-1
,而-1/2
为0
。
答案 2 :(得分:10)
来自C标准:
当整数被分割时,/运算符的结果是 代数商与任何小数部分丢弃.105)如果 商a / b是可表示的,表达式(a / b)* b + a%b应为 等于否则,a / b和%b的行为都是未定义的。
不难想象a的负值不遵循此规则的纯算术移位。 E.g。
(-8191) / 4096 -> -1
(-8191) % 4096 -> -4095
满足等式,而
(-8191) >> 12 -> -2 (assuming arithmetic shifting)
不是截断分割,因此-2 * 4096 - 4095
肯定不等于-8191。
请注意,负数的移位实际上是实现定义的,因此C表达式(-8191) >> 12
根据标准没有一般正确的结果。