ちゃんと実装してみました。
C
1#include <limits.h>
2
3unsigned int rrotate(unsigned int x, int n);
4unsigned int lrotate(unsigned int x, int n);
5
6#define UINT_BIT (sizeof(unsigned int) * CHAR_BIT)
7
8unsigned int rrotate(unsigned int x, int n)
9{
10 n %= UINT_BIT;
11 if (n < 0) n += UINT_BIT;
12 return (x >> n) | (x << (UINT_BIT - n));
13}
14
15unsigned int lrotate(unsigned int x, int n)
16{
17 return rrotate(x, -(n % UINT_BIT));
18}
まず、UINT_BIT
はunsigned int
のビット数です。sizeof
でバイト数を求めて1バイトのビット数であるCHAR_BIT
をかけています。
全ての肝は、(x >> n) | (x << (UINT_BIT - n))
の部分です。x >> n
はx
をn
ビット右にずらす事になりますが、左側のn
ビット分が全て0になってしまいます。ここにちょうど、もともとのx
での右側n
ビット分が入れば良い事になります。x
は全体でUINT_BIT
ありますからUINT_BIT - n
ビット分左にずらせば、ちょうど良く、右側n
ビット分が先頭に来ることになり、残りは0になります。それがx << (UINT_BIT - n)
の部分です。あとは、この二つのビット和を取れば、回転したものになります。なお、負の値は逆方向とも考えられます。つまり、左回転は右回転での負の方向と言うだけです。
n
がUINT_BIT
を超える場合や負の場合は、UINT_BIT
毎回転した場合は同じであるという性質から、適切なUINT_BIT
の倍数を加算または減算すれば良い事になります。つまりそれはUINT_BIT
で割った余りを求めるのと同じです。
さて、これで済んでいれば簡単ですが、C言語特有の動作未定義や実装依存を考慮しなければなりません。
- シフト演算子右辺値が負の値またはビット数を超える場合は動作未定義。
INT_MIN
の-
単項演算子はオーバーフローするため動作未定義。
%
の商が切り詰められる方向は、C99より前は実装依存、C99以降は0の方向。
そういった諸々を考慮した結果、上のコードになります。
【おまけ】
1ビットずつずらす版も作りました。nが大きいととても遅いです。最適化しない場合や、最適化が甘いコンパイラでコンパイルした場合、スタックオーバフローを起こす場合があります。(GCCで-O2なら大丈夫です)
C
1#include <limits.h>
2
3unsigned int rrotate(unsigned int x, int n);
4unsigned int lrotate(unsigned int x, int n);
5
6#define UINT_BIT (sizeof(unsigned int) * CHAR_BIT)
7#define UINT_TOP_BIT (0x1u << (UINT_BIT - 1))
8
9unsigned int rrotate(unsigned int x, int n)
10{
11 if (n == 0)
12 return x;
13 else if (n < 0)
14 return rrotate((x << 1) | (!!(UINT_TOP_BIT & x)), n + 1);
15 else
16 return rrotate((x >> 1) | (UINT_TOP_BIT * (x & 0x1u)), n - 1);
17}
18
19unsigned int lrotate(unsigned int x, int n)
20{
21 if (n < -INT_MAX)
22 return rrotate((x >> 1) | (UINT_TOP_BIT * (x & 0x1u)), -(n + 1));
23 return rrotate(x, -n);
24}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/08/26 23:33