为啥 /=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
不同? cdq
和 sub
在做什么?有必要吗?
【问题讨论】:
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?的主要内容,如果未能解决你的问题,请参考以下文章