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

Posted

技术标签:

【中文标题】为啥 /=2 与有符号整数的 >>=1 不同,并编译为不同的 asm?【英文标题】:Why is /=2 different from >>=1 for signed integers, and compiles to different asm?为什么 /=2 与有符号整数的 >>=1 不同,并编译为不同的 asm? 【发布时间】:2020-06-07 14:32:00 【问题描述】:
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 

im using msvc, // 是该 C 语句的程序集。

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

【问题讨论】:

C 表示它们对负数的舍入方式不同(朝向 0 与朝向 -Inf); asm 必须反映这一点。 因为-7 / 2 = -3>> 1 你会得到-4 大家,非常感谢。我明白他们为什么不同-。 算术右移与逻辑右移。一些 ISA 有一个真实或伪 ASR 指令来处理这个,有些没有,你必须合成它。 ***.com/questions/7622/… 实现定义(我没有检查当前规范)关于 C 编译器是算术还是逻辑编译器,因此某些编译器和/或某些命令行选项上的相同代码可能会产生分而治之的结果相同。实现定义。 【参考方案1】:

将一个负整数除以 2 与将其右移 1 不同。 例如

-7 / 2 = -3

有班次:

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

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

cdq 和 sub 在做什么?有必要吗?

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

【讨论】:

@sunkue,我为cdq添加了解释。 然后sub 表示example-7/2 to(-7-(-1))/2!。这很好玩。多谢。祝你有美好的一天! :)【参考方案2】:

算术右移实际上是除以 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

【讨论】:

奇数除以 2 正好是两个整数的一半。问题是它从两个等距的选择中选择了哪一个。 C99 要求它选择向 0 截断(与算术右移不同,这就像除以 2 并向 -Inf 舍入。) @PeterCordes 这是我写的。现在我说得更清楚了 我不熟悉“最近的绝对整数”对零的含义。这是术语标准吗?谷歌搜索nearest absolute integer 并没有出现任何表明它意味着接近零的东西。所以是的,很好的编辑,它需要澄清。 请注意,“趋于零”是 C99 及更高版本的要求,并且 OP 具​​有不符合 C99 的“im using msvc”。对于 C89,/2>>1 可能相同。

以上是关于为啥 /=2 与有符号整数的 >>=1 不同,并编译为不同的 asm?的主要内容,如果未能解决你的问题,请参考以下文章

无符号与有符号整数的性能

无符号整型与有符号整型相运算规则

在Keil中怎样避免系统自动将无符号值改为有符号值

整数溢出漏洞小结

为啥哈希中的这个字符串键被转换为符号?

常见的进制转换与有符号数据表示法