如何在C中执行旋转移位

如何在C中执行旋转移位,c,assembly,C,Assembly,我有一个如上所述的问题:如何在没有嵌入式汇编的情况下在C中执行旋转移位。更具体地说,如何旋转移位32位int 我现在用类型long-long-int来解决这个问题,但我觉得有点难看,我想知道是否有更优雅的方法 问候。(对未来读者的警告):维基百科的代码产生次优asm(gcc包括分支或cmov)。有关有效的UB自由旋转,请参阅 发件人: unsigned int\u rotl(unsigned int value,int shift){ 如果((移位&=31)==0) 返回值; 返回(值>(32

我有一个如上所述的问题:如何在没有嵌入式汇编的情况下在C中执行旋转移位。更具体地说,如何旋转移位32位
int

我现在用类型
long-long-int
来解决这个问题,但我觉得有点难看,我想知道是否有更优雅的方法

问候。

(对未来读者的警告):维基百科的代码产生次优asm(gcc包括分支或cmov)。有关有效的UB自由旋转,请参阅


发件人:

unsigned int\u rotl(unsigned int value,int shift){
如果((移位&=31)==0)
返回值;
返回(值>(32-shift));
}
无符号整型旋转器(无符号整型值,整型移位){
如果((移位&=31)==0)
返回值;

return(value>>shift)|(value这个答案与我发布的内容重复

有关详细信息,请参阅

在C中表示旋转以避免任何未定义行为的最友好的编译器方式似乎是的实现:

uint32_t rotl32 (uint32_t x, unsigned int n)
{
  const unsigned int mask = (CHAR_BIT*sizeof(x)-1);

  assert ( (n<=mask) &&"rotate by type width or more");
  n &= mask;  // avoid undef behaviour with NDEBUG.  0 overhead for most types / compilers
  return (x<<n) | (x>>( (-n)&mask ));
}
uint32\u t rotl32(uint32\u t x,无符号整数n)
{
常量无符号整数掩码=(字符位*sizeof(x)-1);

assert((n虽然线程很旧,但我想在讨论中加上我的两分钱,并提出我的问题解决方案。希望值得一看,但如果我错了,请纠正我

当我在寻找高效、安全的旋转方式时,我很惊讶没有真正的解决方案。我在这里发现了一些相关的线程: (在C/C++中安全、高效、可移植), 和维基百科风格(涉及分支但安全):

uint32\u t维基百科目录(uint32\u t值,int移位){
如果((移位&=31)==0)
返回值;
返回(值>(32-shift));
}
经过一点思考,我发现模除法符合标准,因为得到的结果总是低于完全符合shift=0,y:(x mod y)
在我们的例子中,每个(x%32)<32这正是我们想要实现的。(是的,我已经经验性地检查了这一点,如果目标有
rotl
指令,您认为最终程序集会使用它吗?如果没有(我怀疑),我想除了内联汇编没有其他方法了?我不知道维基百科是否正确,但当我回顾旋转操作时,如果只是执行(value>>shift)|(值仅当被移位的变量是有符号的时,右移位才应进行符号扩展。如果“值”变量是有符号的,则需要将其强制转换为右移位的无符号。gcc-O3(4.9.2)编译这两个文件以使用轮换。遗憾的是,它没有消除条件,但!可读版本:。此处的不可读版本:
\u rotr
->
movl%esi、%ecx/movl%edi、%eax/andl$31、%ecx/rorl%cl、%eax/testl%ecx、%ecx/cmove%edi、%eax/ret
(AMD64 ABI:在
%rdi
中的第一个参数,在
%rsi
中的第二个参数,在
%eax
中返回)。即使使用
-O0
,gcc也会将移位对转换为旋转insn。@chux:我可以放松旋转计数的类型…
gcc
在x86上生成更好的代码,当
n
是32或64b类型,而不是16或8,但仅使用
int
unsigned
就可以了。很好地捕捉到我as中的逻辑错误sert()不过。@chux:噢,我真傻。我以为你说的是
n
x
,而不是
n
mask
。在对
n
进行了64、32、16和8位类型的实验后,我选择了
uint
作为
n
uint32\code>,发现gcc在
uint>最少的情况下发出了冗余
指令32\u t n
@chux:同意。
未签名的
在我查看asm输出的编译器/平台上已经是32位了。我更新了我的答案,感谢您的帮助完善它。是时候清理了。现在它比@chux:yup更好了,正在处理它:)我正在考虑将变量名更改为
n
=要旋转的值,
c
=旋转计数。但是它与John Regehr的原始值不匹配,上面的一些注释毫无意义。但是许多其他问题使用
n
作为要旋转的值。欢迎使用堆栈溢出!这并不能真正回答问题uestion。如果您正在寻找反馈,您可能希望以更完整的方式编写您的示例,并在上请求评论。请务必先阅读,因为那里的某些事情做得不同!
shift%=32
与John Regehr的
shift&=31
完全相同。可以通过掩蔽实现基-2模y高位。(编译器知道这一点,所以至少在启用优化时,他们会编译相同的版本。)除非您忘记将其应用于
(32移位)
,因此您的版本对于
shift=0
(尽管在大多数编译器上它只会编译为循环)是的,事实上你是对的-我认为shift=32会发生什么,但没有分析shift=0。同时,它会导致'shift&=31'出现相同的未定义行为,在C/C++中正式取消这两个版本的资格,并强制使用“wikipedia”版本(带有检查条件&将产生分支)。我与使用roll生成相当安全代码的gcc进行了检查:#gcc-g-O2-S rot321m1.c_rot132b_i1m:pushl%ebp movl%esp,%ebp movl 8(%ebp),%eax movl 12(%ebp),%ecx和l$31,%ecx roll%cl,%eax leave ret//与John Regehr的班次相同&=31 versionerror:“n”未在此范围内声明。您的意思是n=shift?还是我遗漏了什么?@inc:是的,应该是shift:
return(x(32 shift));
uint32_t rotl32 (uint32_t x, unsigned int n)
{
  const unsigned int mask = (CHAR_BIT*sizeof(x)-1);

  assert ( (n<=mask) &&"rotate by type width or more");
  n &= mask;  // avoid undef behaviour with NDEBUG.  0 overhead for most types / compilers
  return (x<<n) | (x>>( (-n)&mask ));
}
uint32_t wikipedia_rotl(uint32_t value, int shift) {
    if ((shift &= 31) == 0)
      return value;
    return (value << shift) | (value >> (32 - shift));
}
#include <stdint.h>
uint32_t rotl32b_i1m (uint32_t x, uint32_t shift)
    {
    shift %= 32;
    return (x<<shift) | (x>>(32-shift));
    }