どうですか:
uint32_t moveNib(uint32_t x, int n) { return x<<(n<<2) | x>>((8-n)<<2); }
<<2
ニブルからビットへの変換に使用し、ビットをそれだけシフトします。ラップアラウンドを処理するために、反対の方向に反対の量だけシフトされた数値のコピーによる論理和をとります。たとえば、x=0x87654321
とn=1
の場合、左部分は左に 4 ビット シフトされて になり0x76543210
、右部分は右に 28 ビット シフトされて になり0x00000008
、これらを OR すると、結果は0x76543218
要求どおり になります。
編集:-
本当に許可されていない場合、これを使用せずに同じ結果が得られます(2の補数の整数を持つアーキテクチャを想定):
uint32_t moveNib(uint32_t x, int n) { return x<<(n<<2) | x>>((9+~n)<<2); }
編集2:わかりました。以外は使えないのでint
、これはどうですか?
int moveNib(int x, int n) { return (x&0xffffffff)<<(n<<2) | (x&0xffffffff)>>((9+~n)<<2); }
ロジックは前と同じですが、 と AND することにより、計算で符号なし整数を使用するように強制します0xffffffff
。ただし、これはすべて 32 ビット整数を想定しています。私が今見逃しているものは他にありますか?
Edit3:これはもう1つのバージョンです。これはもう少し移植性が高いはずです:
int moveNib(int x, int n) { return ((x|0u)<<((n&7)<<2) | (x|0u)>>((9+~(n&7))<<2))&0xffffffff; }
n
chux で提案されているようにキャップし|0u
、符号付き整数で得られる符号ビットの重複を避けるために、符号なしに変換するために使用します。これは(標準から)次の理由で機能します。
それ以外の場合、符号なし整数型のオペランドのランクが他のオペランドの型のランク以上である場合、符号付き整数型のオペランドは符号なし整数型のオペランドの型に変換されます。
int
と0u
は同じランクですが、符号がないため0u
、0 との OR 演算は null 演算になりますが、結果は符号なしになります。
次に、結果を 32 ビットの範囲に切り捨てて、int
int がこれよりも多くのビットを持っている場合でも関数が機能するようにします (ただし、その場合でもローテーションは最下位の 32 ビットで実行されます。64 ビット バージョンは7 を 15 に、9 を 17 に置き換え、0xffffffffffffffff を使用して切り捨てます)。
このソリューションでは、12 個の演算子を使用します (切り捨てをスキップする場合は 11 個n&7
、変数に格納する場合は 10 個)。
ここで何が起こるかを詳しく見るために、あなたが与えた例について見てみましょう: x=0x87654321
, n=1
. x|0u
符号なしの数値になり0x87654321u
ます。(n&7)<<2=4
は左に 4 ビットシフト((9+~(n&7))<<2=28
し、 は右に 28 ビットシフトします。これをまとめると、 を計算し0x87654321u<<4 | 0x87654321u >> 28
ます。32 ビット整数の場合、これは0x76543210|0x8=0x76543218
. しかし、64 ビット整数の場合は0x876543210|0x8=0x876543218
であるため、その場合は 32 ビットに切り詰める必要があり、これが final の&0xffffffff
動作です。整数が 32 ビットより短い場合、これは機能しませんが、質問の例では 32 ビットだったので、整数型は少なくともその長さであると想定しています。
ちょっとした補足として: リストにない 1 つの演算子 (演算子) を許可するsizeof
と、長い int のすべてのビットを自動的に処理するバージョンを作成できます。Aki に触発されて、次のようになります (16 個の演算子を使用します ( sizeof
C の演算子であることを思い出してください)):
int moveNib(int x, int n) {
int nbit = (n&((sizeof(int)<<1)+~0u))<<2;
return (x|0u)<<nbit | (x|0u)>>((sizeof(int)<<3)+1u+~nbit);
}