C ++中循环移位(旋转)操作的最佳实践

时间:2009-04-22 10:20:47

标签: c++ c rotation bit-manipulation c++-faq

左右移位运算符(<<>>)已在C ++中可用。 但是,我无法找到如何执行循环移位或旋转操作。

如何执行“向左旋转”和“向右旋转”等操作?

在这里向右旋转两次

Initial --> 1000 0011 0100 0010

应该导致:

Final   --> 1010 0000 1101 0000

一个例子会有所帮助。

(编者注:如果旋转计数为零,许多常见的表达方式在C中旋转会受到未定义的行为的影响,或者编译为不止一个旋转机器指令。这个问题的答案应记录最佳实践。)

15 个答案:

答案 0 :(得分:88)

答案 1 :(得分:33)

因为它是C ++,所以使用内联函数:

template <typename INT> 
INT rol(INT val) {
    return (val << 1) | (val >> (sizeof(INT)*CHAR_BIT-1));
}

C ++ 11变体:

template <typename INT> 
constexpr INT rol(INT val) {
    static_assert(std::is_unsigned<INT>::value,
                  "Rotate Left only makes sense for unsigned types");
    return (val << 1) | (val >> (sizeof(INT)*CHAR_BIT-1));
}

答案 2 :(得分:20)

大多数编译器都有内在函数。 Visual Studio例如_rotr8, _rotr16

答案 3 :(得分:16)

明确:

template<class T>
T ror(T x, unsigned int moves)
{
  return (x >> moves) | (x << sizeof(T)*8 - moves);
}

答案 4 :(得分:7)

如何使用标准bitset ...

#include <bitset> 
#include <iostream> 

template <std::size_t N> 
inline void 
rotate(std::bitset<N>& b, unsigned m) 
{ 
   b = b << m | b >> (N-m); 
} 

int main() 
{ 
   std::bitset<8> b(15); 
   std::cout << b << '\n'; 
   rotate(b, 2); 
   std::cout << b << '\n'; 

   return 0;
}

HTH,

答案 5 :(得分:6)

详细信息您可以应用以下逻辑。

如果位模式在整数中是33602

1000 0011 0100 0010

然后你需要翻过2个右边的Shifs然后: 首先制作一个位模式的副本然后左移它:长度 - RightShift 即长度为16,右移值为2 16 - 2 = 14

离开14次后,你得到了。

1000 0000 0000 0000

现在右移33602,根据需要移动2次。 你得到了

0010 0000 1101 0000

现在在左移位值的14倍和右移位值的2倍之间取一个OR。

1000 0000 0000 0000
0010 0000 1101 0000
===================
1010 0000 1101 0000
===================

然后你得到你的转移翻转价值。记住,位操作更快,甚至不需要任何循环。

答案 6 :(得分:5)

如果x是8位值,则可以使用:

x=(x>>1 | x<<7);

答案 7 :(得分:4)

假设您要向右移L位,输入x是一个N位的数字:

unsigned ror(unsigned x, int L, int N) 
{
    unsigned lsbs = x & ((1 << L) - 1);
    return (x >> L) | (lsbs << (N-L));
}

答案 8 :(得分:4)

C ++ 20 std::rotlstd::rotr

已经到了! http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0553r4.html,应将其添加到<bit>标头中。

cppreference表示用法如下:

#include <bit>
#include <bitset>
#include <cstdint>
#include <iostream>

int main()
{
    std::uint8_t i = 0b00011101;
    std::cout << "i          = " << std::bitset<8>(i) << '\n';
    std::cout << "rotl(i,0)  = " << std::bitset<8>(std::rotl(i,0)) << '\n';
    std::cout << "rotl(i,1)  = " << std::bitset<8>(std::rotl(i,1)) << '\n';
    std::cout << "rotl(i,4)  = " << std::bitset<8>(std::rotl(i,4)) << '\n';
    std::cout << "rotl(i,9)  = " << std::bitset<8>(std::rotl(i,9)) << '\n';
    std::cout << "rotl(i,-1) = " << std::bitset<8>(std::rotl(i,-1)) << '\n';
}

给出输出:

i          = 00011101
rotl(i,0)  = 00011101
rotl(i,1)  = 00111010
rotl(i,4)  = 11010001
rotl(i,9)  = 00111010
rotl(i,-1) = 10001110

当支持到达GCC时,我会尝试一下,带有g++-9 -std=c++2a的GCC 9.1.0仍然不支持。

该建议说:

  

标题:

namespace std {
  // 25.5.5, rotating   
  template<class T>
    [[nodiscard]] constexpr T rotl(T x, int s) noexcept;
  template<class T>
    [[nodiscard]] constexpr T rotr(T x, int s) noexcept;

和:

  

25.5.5旋转[bitops.rot]

     

在以下描述中,让N表示std::numeric_limits<T>::digits

template<class T>
  [[nodiscard]] constexpr T rotl(T x, int s) noexcept;
     

约束:T是无符号整数类型(3.9.1 [basic.fundamental])。

     

设为s%N。

     

返回:如果r为0,则x;如果r为正,则(x << r) | (x >> (N - r));如果r为负,则rotr(x, -r)

template<class T>
  [[nodiscard]] constexpr T rotr(T x, int s) noexcept;
     

约束:T是无符号整数类型(3.9.1 [basic.fundamental])。   令r为s%N。

     

返回:如果r为0,则x;如果r为正,则(x >> r) | (x << (N - r));如果r为负,则rotl(x, -r)

还添加了std::popcount来计数1位的数量:How to count the number of set bits in a 32-bit integer?

答案 9 :(得分:3)

正确答案如下:

#define BitsCount( val ) ( sizeof( val ) * CHAR_BIT )
#define Shift( val, steps ) ( steps % BitsCount( val ) )
#define ROL( val, steps ) ( ( val << Shift( val, steps ) ) | ( val >> ( BitsCount( val ) - Shift( val, steps ) ) ) )
#define ROR( val, steps ) ( ( val >> Shift( val, steps ) ) | ( val << ( BitsCount( val ) - Shift( val, steps ) ) ) )

答案 10 :(得分:0)

另一个建议

template<class T>
inline T rotl(T x, unsigned char moves){
    unsigned char temp;
    __asm{
        mov temp, CL
        mov CL, moves
        rol x, CL
        mov CL, temp
    };
    return x;
}

答案 11 :(得分:0)

以下是Dídac Pérez's answer的略微改进版本,其中包含两个方向,以及这些功能的演示&#39;使用unsigned char和unsigned long long值的用法。几点说明:

  1. 内联函数用于编译器优化
  2. 我使用cout << +value技巧简洁地输出我在此处找到的无符号字符:https://stackoverflow.com/a/28414758/1599699
  3. 为了清晰和安全,我建议使用明确的<put the type here>语法。
  4. 我使用unsigned char作为shiftNum参数,因为我在“其他详细信息”部分here中找到了:
  5.   

    如果 additive-expression ,则移位操作的结果是未定义的   否则或 additive-expression 大于或等于   (提升) shift-expression 中的位数。

    以下是我使用的代码:

    #include <iostream>
    
    using namespace std;
    
    template <typename T>
    inline T rotateAndCarryLeft(T rotateMe, unsigned char shiftNum)
    {
        static const unsigned char TBitCount = sizeof(T) * 8U;
    
        return (rotateMe << shiftNum) | (rotateMe >> (TBitCount - shiftNum));
    }
    
    template <typename T>
    inline T rotateAndCarryRight(T rotateMe, unsigned char shiftNum)
    {
        static const unsigned char TBitCount = sizeof(T) * 8U;
    
        return (rotateMe >> shiftNum) | (rotateMe << (TBitCount - shiftNum));
    }
    
    void main()
    {
        //00010100 == (unsigned char)20U
        //00000101 == (unsigned char)5U == rotateAndCarryLeft(20U, 6U)
        //01010000 == (unsigned char)80U == rotateAndCarryRight(20U, 6U)
    
        cout << "unsigned char " << 20U << " rotated left by 6 bits == " << +rotateAndCarryLeft<unsigned char>(20U, 6U) << "\n";
        cout << "unsigned char " << 20U << " rotated right by 6 bits == " << +rotateAndCarryRight<unsigned char>(20U, 6U) << "\n";
    
        cout << "\n";
    
    
        for (unsigned char shiftNum = 0U; shiftNum <= sizeof(unsigned char) * 8U; ++shiftNum)
        {
            cout << "unsigned char " << 21U << " rotated left by " << +shiftNum << " bit(s) == " << +rotateAndCarryLeft<unsigned char>(21U, shiftNum) << "\n";
        }
    
        cout << "\n";
    
        for (unsigned char shiftNum = 0U; shiftNum <= sizeof(unsigned char) * 8U; ++shiftNum)
        {
            cout << "unsigned char " << 21U << " rotated right by " << +shiftNum << " bit(s) == " << +rotateAndCarryRight<unsigned char>(21U, shiftNum) << "\n";
        }
    
    
        cout << "\n";
    
        for (unsigned char shiftNum = 0U; shiftNum <= sizeof(unsigned long long) * 8U; ++shiftNum)
        {
            cout << "unsigned long long " << 3457347ULL << " rotated left by " << +shiftNum << " bit(s) == " << rotateAndCarryLeft<unsigned long long>(3457347ULL, shiftNum) << "\n";
        }
    
        cout << "\n";
    
        for (unsigned char shiftNum = 0U; shiftNum <= sizeof(unsigned long long) * 8U; ++shiftNum)
        {
            cout << "unsigned long long " << 3457347ULL << " rotated right by " << +shiftNum << " bit(s) == " << rotateAndCarryRight<unsigned long long>(3457347ULL, shiftNum) << "\n";
        }
    
        cout << "\n\n";
        system("pause");
    }
    

答案 12 :(得分:0)

--- Substituting RLC in 8051 C for speed --- Rotate left carry
Here is an example using RLC to update a serial 8 bit DAC msb first:
                               (r=DACVAL, P1.4= SDO, P1.5= SCLK)
MOV     A, r
?1:
MOV     B, #8
RLC     A
MOV     P1.4, C
CLR     P1.5
SETB    P1.5
DJNZ    B, ?1

Here is the code in 8051 C at its fastest:
sbit ACC_7  = ACC ^ 7 ; //define this at the top to access bit 7 of ACC
ACC     =   r;
B       =   8;  
do  {
P1_4    =   ACC_7;  // this assembles into mov c, acc.7  mov P1.4, c 
ACC     <<= 1;
P1_5    =   0;
P1_5    =   1;
B       --  ; 
    } while ( B!=0 );
The keil compiler will use DJNZ when a loop is written this way.
I am cheating here by using registers ACC and B in c code.
If you cannot cheat then substitute with:
P1_4    =   ( r & 128 ) ? 1 : 0 ;
r     <<=   1;
This only takes a few extra instructions.
Also, changing B for a local var char n is the same.
Keil does rotate ACC left by ADD A, ACC which is the same as multiply 2.
It only takes one extra opcode i think.
Keeping code entirely in C keeps things simpler sometimes.

答案 13 :(得分:-1)

#define ROTATE_RIGHT(x) ( (x>>1) | (x&1?0x8000:0) )

答案 14 :(得分:-1)

重载功能:

unsigned int rotate_right(unsigned int x)
{
 return (x>>1 | (x&1?0x80000000:0))
}

unsigned short rotate_right(unsigned short x) { /* etc. */ }