如何在C中执行旋转移位

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

我现在在long long int类型的帮助下解决了这个问题,但我觉得它有点难看,想知道是否有更优雅的方法。

亲切的问候。

(警告未来的读者):维基百科的代码产生次优的asm(gcc包括分支或cmov)。 请参阅C ++中循环移位(旋转)操作的最佳实践,以实现高效的无UB旋转。


来自维基百科 :

 unsigned int _rotl(unsigned int value, int shift) { if ((shift &= 31) == 0) return value; return (value << shift) | (value >> (32 - shift)); } unsigned int _rotr(unsigned int value, int shift) { if ((shift &= 31) == 0) return value; return (value >> shift) | (value << (32 - shift)); } 

这个答案与我在针对编译器友好的旋转的Best-practices上发布的内容重复。

有关详细信息,请参阅我在另一个问题上的答案 。

在C中表达旋转以避免任何未定义行为的最适合编译器的方式似乎是John Regehr的实现:

 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)&mask )); } 

适用于任何整数类型,而不仅仅是uint32_t ,因此您可以创建多个版本。 此版本在x86上内联到单个rol %cl, reg (或rol $imm8, reg ),因为编译器知道该指令已经内置了掩码操作。

我建议不要在操作数类型上对其进行模板化,因为当你将一个16位的值存储在一个临时的int时,你不想意外地做一个错误宽度的旋转。 特别是因为整数提升规则可以将涉及窄无符号类型的表达式的结果转换为int

确保对x和返回值使用无符号类型,否则它将不是旋转。 (gcc算术右移,移位符号位的副本而不是零,当你将两个移位的值组合在一起时会导致问题。)

虽然线程已经老了但我想在讨论中加上我的两分钱,并提出解决问题的方法。 希望值得一看,但如果我错了请指正。

当我在寻找高效安全的旋转方式时,我真的很惊讶,没有真正的解决方案。 我在这里找到了一些相关的主题: https : //blog.regehr.org/archives/1063 (C / C ++中的安全,高效和便携式旋转), C ++和维基百科风格的循环移位(旋转)操作的最佳实践 (涉及分支但是安全):

 uint32_t wikipedia_rotl(uint32_t value, int shift) { if ((shift &= 31) == 0) return value; return (value << shift) | (value >> (32 - shift)); } 

经过一些沉思,我发现模数除法符合标准,因为结果提醒总是低于除数,这完全符合<32没有分支的移位条件。 从数学的角度来看:

∀x> = 0,y:(x mod y)

在我们的例子中,每个(x%32)<32这正是我们想要实现的。 (是的,我已经凭经验检查了它总是<32)

 #include  uint32_t rotl32b_i1m (uint32_t x, uint32_t shift) { shift %= 32; return (x<>(32-shift)); } 

另外mod()将简化该过程,因为实际旋转,比方说100位正在旋转全32位3次,这基本上没有任何改变,然后是4位。 那么计算100%32 == 4并旋转4位是不是更好? 无论如何它需要单个处理器操作并将它带到常量值加一个指令的旋转,ok两个因为参数必须从堆栈中获取,但它仍然比使用if()在“wikipedia”方式中分支更好。

那么,你们想到的是什么?