#define USES_ARITHMETIC_SHR(TYPE) ((TYPE)(-1) >> 1 == (TYPE)(-1))
int asr(int value, int amount) /* Better codegen on some older compilers */
{
return !USES_ARITHMETIC_SHR(int) && value < 0 ? ~(~value >> amount) : value >> amount ;
}
int asr2(int value, int amount) /* Completely portable */
{
return value < 0 ? ~(~value >> amount) : value >> amount ;
}
此代码决定是否首先使用内置>>
运算符。您可能想要信任或不信任预处理器,从而为您提供与目标体系结构相同的结果,但安全的后备方案是不信任它。
让我们解释一下value < 0 ? ~(~value >> amount) : value >> amount
部分。
- 如果
value >= 0
那么无论>>
是逻辑还是算术都无关紧要,我们可以使用它。
- 如果
value < 0
then~value
是按位补码,它将是一个正数并且(~value >> amount)
是可移植的(最高amount
位数将被清除,其余的按预期右移)。
~(~value >> amount)
会将所有位翻转回来,包括将顶部amount
的零翻转为一,这正是您想要的算术右移。
假设代码USES_ARITHMETIC_SHR(int) == true
编译-O2
成:
asr(int, int): // x86-64 GCC 4.4.7
mov eax, edi
mov ecx, esi
sar eax, cl
ret
asr(int, int): // x86-64 Clang 3.4.1
mov cl, sil
sar edi, cl
mov eax, edi
ret
asr(int, int): // ARM GCC 4.5.4
mov r0, r0, asr r1
bx lr
这应该是可移植的,但我也不确定它是否真的是迂腐的。如果你不是,你可以#define USES_ARITHMETIC_SHR(TYPE) false
或只是省略检查它,只检查value < 0
. 但这会导致一些较旧的编译器上的代码不太理想。
最新版本的编译器(GCC 8+、Clang 7+)编译这两个版本,asr
并编译asr2
为与上述相同的高效汇编,因此您可以使用任一版本的代码。下面是旧编译器如何使用asr2
,这是一个非常便携的解决方案。
asr2(int, int): // x86-64 GCC 4.4.7
test edi, edi
js .L8
mov eax, edi
mov ecx, esi
sar eax, cl
ret
.L8:
mov eax, edi
mov ecx, esi
not eax
sar eax, cl
not eax
ret
asr2(int, int): // x86-64 Clang 3.4.1
mov cl, sil
sar edi, cl
mov eax, edi
ret
asr2(int, int): // ARM GCC 4.5.4
cmp r0, #0
mvnlt r0, r0
mvnlt r0, r0, asr r1
movge r0, r0, asr r1
bx lr