バイトのビット順序を逆にする方法は複数ありますが、開発者が実装するのに「最も簡単」なものは何なのか、私は知りたいです。そして、逆に言うと、
1110 -> 0111
0010 -> 0100
これは、この PHPの質問と似ていますが、重複していません。
これは、この Cの質問と似ていますが、重複していません。この質問は、開発者が実装する最も簡単な方法を求めています。「最適なアルゴリズム」は、メモリとCPUのパフォーマンスに関係しています。
バイトのビット順序を逆にする方法は複数ありますが、開発者が実装するのに「最も簡単」なものは何なのか、私は知りたいです。そして、逆に言うと、
1110 -> 0111
0010 -> 0100
これは、この PHPの質問と似ていますが、重複していません。
これは、この Cの質問と似ていますが、重複していません。この質問は、開発者が実装する最も簡単な方法を求めています。「最適なアルゴリズム」は、メモリとCPUのパフォーマンスに関係しています。
回答:
1バイトについて話している場合は、何らかの理由で256バイトが使用できない場合を除いて、おそらくテーブル検索が最善の策です。
unsigned int rtable[] = {0x800, 0x4000, ...};
。次に、スクリプトを捨てて、あなたがそれを持っていたことを忘れてください。同等のC ++コードよりもはるかに高速に記述でき、一度しか実行されないため、C ++コードでO(1)ランタイムを取得できます。
これはうまくいくはずです:
unsigned char reverse(unsigned char b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
最初に、左の4ビットが右の4ビットと入れ替えられます。次に、すべての隣接するペアが交換され、次にすべての隣接する単一ビットが交換されます。これにより、順序が逆になります。
ルックアップテーブルは、最も単純な方法の1つである必要があると思います。ただし、完全なルックアップテーブルは必要ありません。
//Index 1==0b0001 => 0b1000
//Index 7==0b0111 => 0b1110
//etc
static unsigned char lookup[16] = {
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf, };
uint8_t reverse(uint8_t n) {
// Reverse the top and bottom nibble then swap them.
return (lookup[n&0b1111] << 4) | lookup[n>>4];
}
// Detailed breakdown of the math
// + lookup reverse of bottom nibble
// | + grab bottom nibble
// | | + move bottom result into top nibble
// | | | + combine the bottom and top results
// | | | | + lookup reverse of top nibble
// | | | | | + grab top nibble
// V V V V V V
// (lookup[n&0b1111] << 4) | lookup[n>>4]
これはコーディングと視覚的な確認がかなり簡単です。
最終的に、これはテーブル全体よりも高速になる可能性があります。ビットarithは安価で、テーブルはキャッシュラインに簡単に適合します。
b0001(1) -> b1000(0x8)
、b0010(2) -> b0100(0x4)
、b1010(10) -> b0101(0x5)
。パターンを見てください。頭の中で計算できるほど簡単です(バイナリを読むことができる場合は、それを実行するために紙が必要になります)。8ビット整数を反転することは、4ビット部分を反転してスワップすることと同じです。私は経験と直感(または魔法)を主張します。
ちょっとしたハックを見る多くのソリューションを。そこからのコピー貼り付けは明らかに実装が簡単です。=)
例(32ビットCPU上):
uint8_t b = byte_to_reverse;
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
「実装するのが簡単」とは、試験や面接で参照なしに実行できることを意味する場合、おそらく最も安全な方法は、ビットを1つずつ別の変数に逆の順序で非効率的にコピーすることです(すでに他の回答で示されています)。 )。
誰も完全なテーブルルックアップソリューションを投稿していないので、これは私のものです。
unsigned char reverse_byte(unsigned char x)
{
static const unsigned char table[] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
return table[x];
}
template <typename T>
T reverse(T n, size_t b = sizeof(T) * CHAR_BIT)
{
assert(b <= std::numeric_limits<T>::digits);
T rv = 0;
for (size_t i = 0; i < b; ++i, n >>= 1) {
rv = (rv << 1) | (n & 0x01);
}
return rv;
}
編集:
オプションのビットカウントを持つテンプレートに変換しました
sizeof(T)*8
てくださいsizeof(T)*CHAR_BITS
。
sizeof(T)*CHAR_BIT
、std::numeric_limits<T>::digits
(ほぼ4年後のペダントリー)に置き換えます。
CHAR_BIT
であるべきではありませんCHAR_BITS
。
おそらく移植性はありませんが、私はアセンブリ言語を使用します。
多くのアセンブリ言語には、ビットをキャリーフラグに回転させ、キャリーフラグをワード(またはバイト)に回転させる命令があります。
アルゴリズムは次のとおりです。
for each bit in the data type:
rotate bit into carry flag
rotate carry flag into destination.
end-for
CとC ++はキャリーへの回転とキャリーからの回転をサポートしていないため、このための高水準言語コードははるかに複雑です。キャリーフラグはモデル化する必要があります。
編集: たとえばアセンブリ言語
; Enter with value to reverse in R0.
; Assume 8 bits per byte and byte is the native processor type.
LODI, R2 8 ; Set up the bit counter
Loop:
RRC, R0 ; Rotate R0 right into the carry bit.
RLC, R1 ; Rotate R1 left, then append carry bit.
DJNZ, R2 Loop ; Decrement R2 and jump if non-zero to "loop"
LODR, R0 R1 ; Move result into R0.
次の解決策は、ここで見た他のビット操作アルゴリズムよりも簡単だと思います。
unsigned char reverse_byte(char a)
{
return ((a & 0x1) << 7) | ((a & 0x2) << 5) |
((a & 0x4) << 3) | ((a & 0x8) << 1) |
((a & 0x10) >> 1) | ((a & 0x20) >> 3) |
((a & 0x40) >> 5) | ((a & 0x80) >> 7);
}
最初のバイトから最後のバイトまで、バイトのすべてのビットを取得し、それに応じてシフトします。
説明:
((a & 0x1) << 7) //get first bit on the right and shift it into the first left position
| ((a & 0x2) << 5) //add it to the second bit and shift it into the second left position
//and so on
最も簡単な方法は、ループ内のビット位置を反復処理するために考えられます。
unsigned char reverse(unsigned char c) {
int shift;
unsigned char result = 0;
for (shift = 0; shift < CHAR_BIT; shift++) {
if (c & (0x01 << shift))
result |= (0x80 >> shift);
}
return result;
}
CHAR_BIT
を想定char
しているのになぜ使用するのですか?
一定の8ビット入力の非常に限られたケースでは、このメソッドは実行時にメモリやCPUを必要としません。
#define MSB2LSB(b) (((b)&1?128:0)|((b)&2?64:0)|((b)&4?32:0)|((b)&8?16:0)|((b)&16?8:0)|((b)&32?4:0)|((b)&64?2:0)|((b)&128?1:0))
これをARINC-429に使用しました。ラベルのビットオーダー(エンディアン)は、残りの単語の反対です。ラベルは定数であることが多く、通常は8進数です。
仕様ではこのラベルをビッグエンディアン205の8進数として定義しているため、これを使用して定数を定義しています。
#define LABEL_HF_COMM MSB2LSB(0205)
その他の例:
assert(0b00000000 == MSB2LSB(0b00000000));
assert(0b10000000 == MSB2LSB(0b00000001));
assert(0b11000000 == MSB2LSB(0b00000011));
assert(0b11100000 == MSB2LSB(0b00000111));
assert(0b11110000 == MSB2LSB(0b00001111));
assert(0b11111000 == MSB2LSB(0b00011111));
assert(0b11111100 == MSB2LSB(0b00111111));
assert(0b11111110 == MSB2LSB(0b01111111));
assert(0b11111111 == MSB2LSB(0b11111111));
assert(0b10101010 == MSB2LSB(0b01010101));
あなたはstd::vector<bool>
(ビットパックされている)に興味があるかもしれませんstd::bitset
それは要求されるように最も単純でなければなりません。
#include <iostream>
#include <bitset>
using namespace std;
int main() {
bitset<8> bs = 5;
bitset<8> rev;
for(int ii=0; ii!= bs.size(); ++ii)
rev[bs.size()-ii-1] = bs[ii];
cerr << bs << " " << rev << endl;
}
他のオプションの方が速い場合があります。
編集:私はあなたに解決策を借りています std::vector<bool>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<bool> b{0,0,0,0,0,1,0,1};
reverse(b.begin(), b.end());
copy(b.begin(), b.end(), ostream_iterator<int>(cerr));
cerr << endl;
}
2番目の例では、c ++ 0x拡張が必要です(配列をで初期化するため{...}
)。a bitset
またはa std::vector<bool>
(またはboost::dynamic_bitset
)を使用する利点は、バイトまたはワードに限定されず、任意の数のビットを反転できることです。
HTH
std::vector<bool> b = { ... }; std::vector<bool> rb ( b.rbegin(), b.rend());
-逆反復子を直接使用しますか?
「最も簡単な方法」の意味に応じて、ビットを反転する多くの方法があります。
おそらく最も論理的な方法は、最初のビットにマスクを適用しながらバイトを回転させることです(n & 1)
。
unsigned char reverse_bits(unsigned char b)
{
unsigned char r = 0;
unsigned byte_len = 8;
while (byte_len--) {
r = (r << 1) | (b & 1);
b >>= 1;
}
return r;
}
1)unsigner charの長さは1バイト(8ビットに等しい)であるため、各ビットをスキャンすることを意味します while (byte_len--)
2)まず、bが右端のビットであるかどうかをでチェックし(b & 1)
ます。もしそうなら、rのビット1を|
し、rに2を掛けて1ビットだけ左に移動します。(r << 1)
3)次に、符号なしchar bを2で除算してb >>=1
、変数bの右端にあるビットを消去します。注意として、b >> = 1; b / = 2と同等です。
この解決策は 、プログラミングハックセクションのRich Schroeppel
unsigned char reverse_bits3(unsigned char b)
{
return (b * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff;
}
1)乗算演算(b * 0x0202020202ULL)は、8ビットバイトパターンの5つの個別のコピーを作成して、64ビット値にファンアウトします。
2)AND演算(&0x010884422010ULL)は、10ビットの各ビットグループに対して、正しい(逆の)位置にあるビットを選択します。
3)乗算とAND演算は、元のバイトからビットをコピーして、それぞれが10ビットセットの1つだけに現れるようにします。元のバイトから反転したビットの位置は、10ビットセット内の相対位置と一致します。
4)2 ^ 10-1によるモジュラス除算を含む最後のステップ(%0x3ff)には、10ビットの各セット(位置0-9、10-19、20-29など)をマージする効果があります。 64ビット値。それらはオーバーラップしないため、モジュラス除算の基礎となる追加ステップはOR演算のように動作します。
unsigned char reverse(unsigned char b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
これは最も賛成された回答であり、いくつかの説明にもかかわらず、ほとんどの人にとって、0xF0、0xCC、0xAA、0x0F、0x33、および0x55が本当に意味することを視覚化することは難しいと思います。
これはGCC拡張である「0b」を利用せず、2014年12月にリリースされたC ++ 14標準以降に含まれているので、この回答のしばらく後に2010年4月からさかのぼります
整数定数は、一連の「0」と「1」の数字で構成され、「0b」または「0B」が前に付いたバイナリ定数として記述できます。これは、ビットレベルで多く動作する環境(マイクロコントローラーなど)で特に役立ちます。
以下のコードスニペットを確認して、半分ずつ移動するこのソリューションを覚えて理解してください。
unsigned char reverse(unsigned char b) {
b = (b & 0b11110000) >> 4 | (b & 0b00001111) << 4;
b = (b & 0b11001100) >> 2 | (b & 0b00110011) << 2;
b = (b & 0b10101010) >> 1 | (b & 0b01010101) << 1;
return b;
}
注意:これ>> 4
は、1バイトに8ビットがあり、それが符号なし文字であるため、残りの半分を取得するためです。
このソリューションは、2行を追加するだけで同じロジックに従って、4バイトに簡単に適用できます。両方のマスクは互いに補完し合うため、ビットを切り替えてインクを節約するために〜を使用することもできます。
uint32_t reverse_integer_bits(uint32_t b) {
uint32_t mask = 0b11111111111111110000000000000000;
b = (b & mask) >> 16 | (b & ~mask) << 16;
mask = 0b11111111000000001111111100000000;
b = (b & mask) >> 8 | (b & ~mask) << 8;
mask = 0b11110000111100001111000011110000;
b = (b & mask) >> 4 | (b & ~mask) << 4;
mask = 0b11001100110011001100110011001100;
b = (b & mask) >> 2 | (b & ~mask) << 2;
mask = 0b10101010101010101010101010101010;
b = (b & mask) >> 1 | (b & ~mask) << 1;
return b;
}
上記のロジックは、あらゆるタイプの符号なしで機能するループで要約できます。
template <class T>
T reverse_bits(T n) {
short bits = sizeof(n) * 8;
T mask = ~T(0); // equivalent to uint32_t mask = 0b11111111111111111111111111111111;
while (bits >>= 1) {
mask ^= mask << (bits); // will convert mask to 0b00000000000000001111111111111111;
n = (n & ~mask) >> bits | (n & mask) << bits; // divide and conquer
}
return n;
}
上記の関数を含めて自分で試してください:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
template <class T>
void print_binary(T n)
{ T mask = 1ULL << ((sizeof(n) * 8) - 1); // will set the most significant bit
for(; mask != 0; mask >>= 1) putchar('0' | !!(n & mask));
putchar('\n');
}
int main() {
uint32_t n = 12;
print_binary(n);
n = reverse_bits(n);
print_binary(n);
unsigned char c = 'a';
print_binary(c);
c = reverse_bits(c);
print_binary(c);
uint16_t s = 12;
print_binary(s);
s = reverse_bits(s);
print_binary(s);
uint64_t l = 12;
print_binary(l);
l = reverse_bits(l);
print_binary(l);
return 0;
}
最後に重要なことですが、もし最も単純な行数が少ないということであれば、インラインアセンブリを試してみませんか?
-masm=intel
コンパイル時に追加することで、以下のコードスニペットをテストできます。
unsigned char reverse_bits(unsigned char c) {
__asm__ __volatile__ (R"(
mov cx, 8
daloop:
ror di
adc ax, ax
dec cx
jnz short daloop
;)");
}
行ごとの説明:
mov cx, 8 ; we will reverse the 8 bits contained in one byte
daloop: ; while loop
shr di ; Shift Register `di` (containing value of the first argument of callee function) to the Right
rcl ax ; Rotate Carry Left: rotate ax left and add the carry from shr di, the carry is equal to 1 if one bit was "lost" from previous operation
dec cl ; Decrement cx
jnz short daloop; Jump if cx register is Not equal to Zero, else end loop and return value contained in ax register
遅いがシンプルな実装:
static int swap_bit(unsigned char unit)
{
/*
* swap bit[7] and bit[0]
*/
unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01)) << 7) | (unit & 0x7f));
unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01))) | (unit & 0xfe));
unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01)) << 7) | (unit & 0x7f));
/*
* swap bit[6] and bit[1]
*/
unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02)) << 5) | (unit & 0xbf));
unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02))) | (unit & 0xfd));
unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02)) << 5) | (unit & 0xbf));
/*
* swap bit[5] and bit[2]
*/
unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04)) << 3) | (unit & 0xdf));
unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04))) | (unit & 0xfb));
unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04)) << 3) | (unit & 0xdf));
/*
* swap bit[4] and bit[3]
*/
unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08)) << 1) | (unit & 0xef));
unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08))) | (unit & 0xf7));
unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08)) << 1) | (unit & 0xef));
return unit;
}
これは高速な解決策ですか?
int byte_to_be_reversed =
((byte_to_be_reversed>>7)&0x01)|((byte_to_be_reversed>>5)&0x02)|
((byte_to_be_reversed>>3)&0x04)|((byte_to_be_reversed>>1)&0x08)|
((byte_to_be_reversed<<7)&0x80)|((byte_to_be_reversed<<5)&0x40)|
((byte_to_be_reversed<<3)&0x20)|((byte_to_be_reversed<<1)&0x10);
forループを使用する煩わしさを解消します。しかし、これが効率的で速いかどうか専門家に教えてください?
この単純な関数は、マスクを使用して入力バイトの各ビットをテストし、それをシフト出力に転送します。
char Reverse_Bits(char input)
{
char output = 0;
for (unsigned char mask = 1; mask > 0; mask <<= 1)
{
output <<= 1;
if (input & mask)
output |= 1;
}
return output;
}
これはBobStein-VisiBoneが提供するものに基づいています
#define reverse_1byte(b) ( ((uint8_t)b & 0b00000001) ? 0b10000000 : 0 ) | \
( ((uint8_t)b & 0b00000010) ? 0b01000000 : 0 ) | \
( ((uint8_t)b & 0b00000100) ? 0b00100000 : 0 ) | \
( ((uint8_t)b & 0b00001000) ? 0b00010000 : 0 ) | \
( ((uint8_t)b & 0b00010000) ? 0b00001000 : 0 ) | \
( ((uint8_t)b & 0b00100000) ? 0b00000100 : 0 ) | \
( ((uint8_t)b & 0b01000000) ? 0b00000010 : 0 ) | \
( ((uint8_t)b & 0b10000000) ? 0b00000001 : 0 )
コンパイラーが自動的に作業を処理するので、これがとても好きです。それ以上のリソースは必要ありません。
これも16ビットに拡張できます...
#define reverse_2byte(b) ( ((uint16_t)b & 0b0000000000000001) ? 0b1000000000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000000010) ? 0b0100000000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000000100) ? 0b0010000000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000001000) ? 0b0001000000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000010000) ? 0b0000100000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000100000) ? 0b0000010000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000001000000) ? 0b0000001000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000010000000) ? 0b0000000100000000 : 0 ) | \
( ((uint16_t)b & 0b0000000100000000) ? 0b0000000010000000 : 0 ) | \
( ((uint16_t)b & 0b0000001000000000) ? 0b0000000001000000 : 0 ) | \
( ((uint16_t)b & 0b0000010000000000) ? 0b0000000000100000 : 0 ) | \
( ((uint16_t)b & 0b0000100000000000) ? 0b0000000000010000 : 0 ) | \
( ((uint16_t)b & 0b0001000000000000) ? 0b0000000000001000 : 0 ) | \
( ((uint16_t)b & 0b0010000000000000) ? 0b0000000000000100 : 0 ) | \
( ((uint16_t)b & 0b0100000000000000) ? 0b0000000000000010 : 0 ) | \
( ((uint16_t)b & 0b1000000000000000) ? 0b0000000000000001 : 0 )
b
単一の数値よりも複雑な式の場合は、かっこでREVERSE_BYTE
囲みます。おそらく、マクロの名前を、より複雑な(実行時の)式にしたくないヒントに変更します。または、インライン関数にします。(しかし、全体的に私はこれが非常に単純で、エラーの可能性がほとんどなく、メモリから簡単に実行できるので
小型のマイクロコントローラーを使用していて、フットプリントの小さい高速ソリューションが必要な場合は、これがソリューションになる可能性があります。Cプロジェクトで使用することは可能ですが、このファイルをアセンブラファイル* .asmとしてCプロジェクトに追加する必要があります。手順:Cプロジェクトで、次の宣言を追加します。
extern uint8_t byte_mirror(uint8_t);
Cからこの関数を呼び出す
byteOutput= byte_mirror(byteInput);
これはコードであり、8051コアにのみ適しています。CPUレジスタには、r0がbyteInputからのデータです。コードは右にr0のキャリーを交差させ、次にキャリーを左に回転させてr1にします。この手順をビットごとに8回繰り返します。次に、レジスタr1がbyteOutputとしてc関数に返されます。8051において、コアは、回転acumulator用にのみposibbleあるA。
NAME BYTE_MIRROR
RSEG RCODE
PUBLIC byte_mirror //8051 core
byte_mirror
mov r3,#8;
loop:
mov a,r0;
rrc a;
mov r0,a;
mov a,r1;
rlc a;
mov r1,a;
djnz r3,loop
mov r0,a
ret
長所:フットプリントが小さく、高速です短所:再利用可能なコードではなく、8051専用です
011101101->キャリー
101101110 <キャリー
typedef struct
{
uint8_t b0:1;
uint8_t b1:1;
uint8_t b2:1;
uint8_t b3:1;
uint8_t b4:1;
uint8_t b5:1;
uint8_t b6:1;
uint8_t b7:1;
} bits_t;
uint8_t reverse_bits(uint8_t src)
{
uint8_t dst = 0x0;
bits_t *src_bits = (bits_t *)&src;
bits_t *dst_bits = (bits_t *)&dst;
dst_bits->b0 = src_bits->b7;
dst_bits->b1 = src_bits->b6;
dst_bits->b2 = src_bits->b5;
dst_bits->b3 = src_bits->b4;
dst_bits->b4 = src_bits->b3;
dst_bits->b5 = src_bits->b2;
dst_bits->b6 = src_bits->b1;
dst_bits->b7 = src_bits->b0;
return dst;
}
uint8_t
1ビットのフィールドの使用は少し醜いです。なぜなら、最初は8ビットが必要だと言われているようですが、行の終わりで1ビットのみと定義しているためです。私はunsigned b0:1
等を使用します
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
unsigned char rev = 0x70 ; // 0b01110000
unsigned char tmp = 0;
for(i=0;i<8;i++)
{
tmp |= ( ((rev & (1<<i))?1:0) << (7-i));
}
rev = tmp;
printf("%x", rev); //0b00001110 binary value of given number
return 0;
}
これは十分簡単だと思います
uint8_t reverse(uint8_t a)
{
unsigned w = ((a << 7) & 0x0880) | ((a << 5) & 0x0440) | ((a << 3) & 0x0220) | ((a << 1) & 0x0110);
return static_cast<uint8_t>(w | (w>>8));
}
または
uint8_t reverse(uint8_t a)
{
unsigned w = ((a & 0x11) << 7) | ((a & 0x22) << 5) | ((a & 0x44) << 3) | ((a & 0x88) << 1);
return static_cast<uint8_t>(w | (w>>8));
}
unsigned char c ; // the original
unsigned char u = // the reversed
c>>7&0b00000001 |
c<<7&0b10000000 |
c>>5&0b00000010 |
c<<5&0b01000000 |
c>>3&0b00000100 |
c<<3&0b00100000 |
c>>1&0b00001000 |
c<<1&0b00010000 ;
Explanation: exchanged bits as per the arrows below.
01234567
<------>
#<---->#
##<-->##
###<>###
これまでの回答ではこのようなものを見つけることができないので、私は自分の解決策にチップを入れます。多分少しオーバーエンジニアリングされていますがstd::index_sequence
、コンパイル時にC ++ 14を使用してルックアップテーブルを生成します。
#include <array>
#include <utility>
constexpr unsigned long reverse(uint8_t value) {
uint8_t result = 0;
for (std::size_t i = 0, j = 7; i < 8; ++i, --j) {
result |= ((value & (1 << j)) >> j) << i;
}
return result;
}
template<size_t... I>
constexpr auto make_lookup_table(std::index_sequence<I...>)
{
return std::array<uint8_t, sizeof...(I)>{reverse(I)...};
}
template<typename Indices = std::make_index_sequence<256>>
constexpr auto bit_reverse_lookup_table()
{
return make_lookup_table(Indices{});
}
constexpr auto lookup = bit_reverse_lookup_table();
int main(int argc)
{
return lookup[argc];
}
私はこの質問に日付が付いていることを知っていますが、このトピックはいくつかの目的に関連していると思います。これは非常にうまく機能し、読みやすいバージョンです。これが最も高速または最も効率的であるとは言えませんが、最もクリーンなものの1つである必要があります。また、ビットパターンを簡単に表示するためのヘルパー関数も追加しました。この関数は、独自のビットマニピュレータを作成する代わりに、いくつかの標準ライブラリ関数を使用します。
#include <algorithm>
#include <bitset>
#include <exception>
#include <iostream>
#include <limits>
#include <string>
// helper lambda function template
template<typename T>
auto getBits = [](T value) {
return std::bitset<sizeof(T) * CHAR_BIT>{value};
};
// Function template to flip the bits
// This will work on integral types such as int, unsigned int,
// std::uint8_t, 16_t etc. I did not test this with floating
// point types. I chose to use the `bitset` here to convert
// from T to string as I find it easier to use than some of the
// string to type or type to string conversion functions,
// especially when the bitset has a function to return a string.
template<typename T>
T reverseBits(T& value) {
static constexpr std::uint16_t bit_count = sizeof(T) * CHAR_BIT;
// Do not use the helper function in this function!
auto bits = std::bitset<bit_count>{value};
auto str = bits.to_string();
std::reverse(str.begin(), str.end());
bits = std::bitset<bit_count>(str);
return static_cast<T>( bits.to_ullong() );
}
// main program
int main() {
try {
std::uint8_t value = 0xE0; // 1110 0000;
std::cout << +value << '\n'; // don't forget to promote unsigned char
// Here is where I use the helper function to display the bit pattern
auto bits = getBits<std::uint8_t>(value);
std::cout << bits.to_string() << '\n';
value = reverseBits(value);
std::cout << +value << '\n'; // + for integer promotion
// using helper function again...
bits = getBits<std::uint8_t>(value);
std::cout << bits.to_string() << '\n';
} catch(const std::exception& e) {
std::cerr << e.what();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
そして、それは次の出力を与えます。
224
11100000
7
00000111
これは、8x8ドットマトリックスセットの配列で私を助けました。
uint8_t mirror_bits(uint8_t var)
{
uint8_t temp = 0;
if ((var & 0x01))temp |= 0x80;
if ((var & 0x02))temp |= 0x40;
if ((var & 0x04))temp |= 0x20;
if ((var & 0x08))temp |= 0x10;
if ((var & 0x10))temp |= 0x08;
if ((var & 0x20))temp |= 0x04;
if ((var & 0x40))temp |= 0x02;
if ((var & 0x80))temp |= 0x01;
return temp;
}