为什么 /=2 与有符号整数的 >>=1 不同,并编译为不同的 asm?

Why is /=2 different from >>=1 for signed integers, and compiles to different asm?

unsigned int a=200;
//mov   dword ptr [a],0C8h  
a >>= 1;
//mov   eax,dword ptr [a]  
//shr   eax,1  
//mov   dword ptr [a],eax  
a /= 2;
//mov   eax,dword ptr [a]  
//shr   eax,1  
//mov   dword ptr [a],eax  
int b = -200;
//mov   dword ptr [b],0FFFFFF38h  
b /= 2;
//mov   eax,dword ptr [b]  
//cdq  
//sub   eax,edx  
//sar   eax,1  
//mov   dword ptr [b],eax  
b >>= 1;
//mov   eax,dword ptr [b]  
//sar   eax,1  
//mov   dword ptr [b],eax 

我使用的是 msvc,// 是该 C 语句的程序集。

为什么 signed int /=2>>=1 不同? cdqsub 在做什么?有必要吗?

负整数除以 2 与右移 1 不同。 例如

-7 / 2 = -3

轮班:

11111001b >> 1 = 11111100b which is -4

因此编译器必须处理整数为负数的情况

What are cdq and sub doing? Are they necessary?

cdq 执行以下 EDX:EAX ← EAX 的符号扩展。

因此,如果 EAX 中的值为负数,EDX 将得到 0xFFFFFFFF(即 -1),否则将为 0(由于 EAX 的符号扩展)。

sub eax, edx ; will either result in 'eax - 0' (if EAX is positive) or
             ;                       'eax - (-1)' (if EAX is negative)

在上面的示例中,将 -7 规范化为 -7 - (-1) = -6,然后是 -6 >> 1 = -3

算术右移实际上是除以 2,但四舍五入为最接近的较小整数。所以 -7 >> 1-4

除以二的数学除法(根据 C 标准的要求)四舍五入为最接近的绝对整数(即接近零)。

代码正在编译为另一组指令:

        mov     edx, DWORD PTR x
        mov     eax, edx
        shr     eax, 31
        add     eax, edx
        sar     eax
        mov     DWORD PTR x, eax

https://godbolt.org/z/No6u6V