当我用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
它干净简洁,用逻辑右移取代了二的幂除。
但是当我编译这段代码时:
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
在进行算术右移之前执行一些操作。
为什么需要这些额外的操作?为什么算术移位还不够?
3条答案
按热度按时间esyap4oy1#
原因是无符号除以2^n可以非常简单地实现,而有符号除法稍微复杂一些。
对于
u
的所有可能值,u / 4096
等效于u >> 12
。v / 4096
NOT等价于v >> 12
-当v < 0
时,它会崩溃,因为当涉及负数时,移位和除法的舍入方向不同。cig3rfwq2#
“额外操作”补偿了算术右移将结果向负无穷舍入而除法将结果向零舍入的事实。
例如,
-1 >> 1
是-1
,而-1/2
是0
。jgwigjjp3#
根据C标准:
当整数被除时,/运算符的结果是去掉任何小数部分的代数商。如果商a/b是可表示的,则表达式(a/b)*b + a%b应等于a;否则,a/b和a%b的行为都未定义。
不难想到,在纯算术移位中,a的负值不遵循这个规则。
它满足方程,而
不是舍位除法,因此
-2 * 4096 - 4095
肯定不等于-8191。请注意,负数的移位实际上是由实现定义的,因此C表达式
(-8191) >> 12
按照标准通常没有正确的结果。