web-dev-qa-db-ja.com

C ++での循環シフト(回転)操作のベストプラクティス

左および右シフト演算子(<<および>>)は、C++ですでに使用可能です。ただし、循環シフトまたは回転操作を実行する方法を見つけることができませんでした。

「左回転」や「右回転」などの操作はどのように実行できますか?

ここで右に2回回転

Initial --> 1000 0011 0100 0010

結果は次のとおりです。

Final   --> 1010 0000 1101 0000

例が役立ちます。

(編集者注:Cで回転を表現する多くの一般的な方法は、回転カウントがゼロの場合、または単一の回転機械命令以上にコンパイルされる場合、未定義の動作に悩まされます。この質問の答えはベストプラクティスを文書化する必要があります。)

81
Elroy

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));
}
33
MSalters

ほとんどのコンパイラには、そのための組み込み関数があります。たとえばVisual Studio _ rotr8、_rotr16

20
VolkerK

明確に:

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

標準的なビットセットを使用して、このようなものをどうやって...

#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、

7
Abhay

詳細には、次のロジックを適用できます。

ビットパターンが整数で33602の場合

 1000 0011 0100 0010 

そして、2つの右shifsでロールオーバーする必要があります:最初にビットパターンのコピーを作成し、次に左シフトします:長さ-RightShiftつまり、長さは16です。右シフト値は2 16-2 = 14です。

14回左シフトすると、取得します。

 1000 0000 0000 0000 

必要に応じて、値33602を2回右にシフトします。あなたが得る

 0010 0000 1101 0000 

次に、ORを左に14回シフトした値と右に2回シフトした値の間で取ります。

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

そして、シフトしたロールオーバー値を取得します。ビット単位の演算は高速であり、ループも必要ないことを忘れないでください。

6
S M Kamran

Xが8ビット値の場合、これを使用できます。

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

Lビットだけ右にシフトし、入力xNビットの数値であると仮定します。

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

正解は次のとおりです。

#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 ) ) ) )
3
user3102555

C++ 20 _std::rotl_および_std::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にサポートが到着したら試してみますが、GCC 9.1.0の_g++-9 -std=c++2a_はまだサポートしていません。

提案は言う:

ヘッダ:

_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])。

Rを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ビットの数をカウントするために追加されました: 32ビット整数のセットビットの数をカウントする方法?

以下は DídacPérezの答え のわずかに改善されたバージョンであり、両方向が実装されており、unsigned charおよびunsigned long long値を使用したこれらの関数の使用法のデモがあります。いくつかのメモ:

  1. 関数はコンパイラーの最適化のためにインライン化されています
  2. ここで見つけた符号なしcharを数値で簡潔に出力するためにcout << +valueトリックを使用しました: https://stackoverflow.com/a/28414758/1599699
  3. 明確さと安全性のために、明示的な<put the type here>構文を使用することをお勧めします。
  4. 「追加の詳細」セクションで見つけたもののため、shiftNumパラメーターに符号なし文字を使用しました here

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");
}
0
Andrew
--- 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.
0
MikeZ

ソースコードxビット数

int x =8;
data =15; //input
unsigned char tmp;
for(int i =0;i<x;i++)
{
printf("Data & 1    %d\n",data&1);
printf("Data Shifted value %d\n",data>>1^(data&1)<<(x-1));
tmp = data>>1|(data&1)<<(x-1);
data = tmp;  
}
0
kjk

別の提案

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;
}
0
SalemD