C / C ++でバイトのビットの順序を逆にする最も簡単な方法は何ですか?


110

バイトのビット順序を逆にする方法は複数ありますが、開発者が実装するのに「最も簡単」なものは何なのか、私は知りたいです。そして、逆に言うと、

1110 -> 0111
0010 -> 0100

これは、この PHPの質問と似ていますが、重複していません。

これは、この Cの質問と似ていますが、重複していません。この質問は、開発者が実装する最も簡単な方法を求めています。「最適なアルゴリズム」は、メモリとCPUのパフォーマンスに関係しています。


インラインアセンブリを使用します。関数を別の翻訳単位に入れてください。ターゲットプラットフォームごとに1つのアセンブリ言語モジュールを用意します。ビルドプロセスにモジュールを選択させます。
Thomas Matthews

@Andreas最も簡単な実装
ネイサン

回答:


102

1バイトについて話している場合は、何らかの理由で256バイトが使用できない場合を除いて、おそらくテーブル検索が最善の策です。


12
既製のソリューションをコピーせずに実装が簡単なものについて話している場合でも、ルックアップテーブルを作成するには別のソリューションが必要です。(もちろん、手動で行うこともできますが、エラーが発生しやすく、時間がかかります…)
Arkku 2010

7
パリンドロームを無視する場合は、配列を256バイトより少し少なく絞ることができます。
ヴィルヘルムテル

8
@wilhelmtell-どのものがパリンドロームであるかを知るためのテーブルが必要です。
Mark Ransom

6
@wilhelmtell:ええと、スクリプトを作成するにはまだ別のソリューション必要です。それが私のポイントでした。ルックアップテーブルは簡単に使用できますが、作成は簡単ではありません。(既製のルックアップテーブルをコピーする場合を除いて、ソリューションをコピーすることもできます。)たとえば、「最も簡単な」ソリューションが試験または面接で紙に書くことができると考えられる場合、私は手動でルックアップテーブルの作成を開始し、それを実行するプログラムを作成すると、既に別のソリューションが含まれます(ソリューションとテーブルの両方を含むソリューションよりも単独で簡単になります)。
Arkku

4
@Arkkuは、最初の256バイトのテーブルとその逆マッピングを出力するスクリプトを書くことを意味しています。はい、reverse関数の作成に戻りましたが、今ではお気に入りのスクリプト言語で記述されているので、好きなだけ嫌なことができます。完了するとすぐに捨てて、一度実行しただけです。スクリプトの出力をCコードとして作成しますunsigned int rtable[] = {0x800, 0x4000, ...};。次に、スクリプトを捨てて、あなたがそれを持っていたことを忘れてください。同等のC ++コードよりもはるかに高速に記述でき、一度しか実行されないため、C ++コードでO(1)ランタイムを取得できます。
wilhelmtell 2010

227

これはうまくいくはずです:

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ビットと入れ替えられます。次に、すべての隣接するペアが交換され、次にすべての隣接する単一ビットが交換されます。これにより、順序が逆になります。


26
かなり短くて迅速ですが、単純ではありません。
Mark Ransom

3
このアプローチは、エンディアンのバイトスワッピングを実行するために明確に一般化します。
Boojum

2
最も単純なアプローチではありませんが、私はそれが好きです+1。
ネイサン

7
はい、簡単です。これは一種の分割統治アルゴリズムです。優れた!
kiewic

以下の@Arkkuが提案する方法よりも高速ですか?
2013年

122

ルックアップテーブルは、最も単純な方法の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は安価で、テーブルはキャッシュラインに簡単に適合します。


10
これは、テーブルソリューションの複雑さを軽減する優れた方法です。+1
e.James、

3
いいですが、キャッシュミスが発生します。
Johan Kotlinski、2010年

7
@kotlinski:キャッシュミスの原因は何ですか?小さなテーブルバージョンは、大きなテーブルバージョンよりもキャッシュ効率が良いと思います。私のCore2では、キャッシュラインは64バイト幅です。テーブル全体が複数のラインにまたがるのに対して、小さいテーブルは1つのラインに簡単に適合します。
deft_code

4
@kotlinski:一時的な局所性は、アドレスの局所性よりもキャッシュヒットまたは置換戦略にとって重要です
cfi

6
@Harshdeep:テーブルエントリのバイナリエンコードされたインデックスを検討します。インデックスb0000(0)-> b0000(0x0)退屈; b0001(1) -> b1000(0x8)b0010(2) -> b0100(0x4)b1010(10) -> b0101(0x5)。パターンを見てください。頭の中で計算できるほど簡単です(バイナリを読むことができる場合は、それを実行するために紙が必要になります)。8ビット整数を反転することは、4ビット部分を反転してスワップすることと同じです。私は経験と直感(または魔法)を主張します。
deft_code 2014年

46

ちょっとしたハックを見る多くのソリューションを。そこからのコピー貼り付けは明らかに実装が簡単です。=)

例(32ビットCPU上):

uint8_t b = byte_to_reverse;
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;

「実装するのが簡単」とは、試験や面接で参照なしに実行できることを意味する場合、おそらく最も安全な方法は、ビットを1つずつ別の変数に逆の順序で非効率的にコピーすることです(すでに他の回答で示されています)。 )。


1
URLから:32ビットCPU:b =((b * 0x0802LU&0x22110LU)|(b * 0x8020LU&0x88440LU))* 0x10101LU >> 16;
ジョシュア

1
@ジョシュア:それは私の個人的なお気に入りでもあります。(リンクされたページで述べられているように)警告は、uint8_tに割り当てまたはキャストする必要があることです。そうしないと、上位ビットにゴミが発生します。
Arkku

41

誰も完全なテーブルルックアップソリューションを投稿していないので、これは私のものです。

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];
}

2
便利、ありがとう。私の遅いシフト方法は埋め込みアプリのパフォーマンスを制限していたようです。PICのROMにテーブルを配置(romキーワードを追加)。
12


25
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;
}

編集:

オプションのビットカウントを持つテンプレートに変換しました


@nvl-修正されました。テンプレートとして作成し始めましたが、途中で作成しないことを決定しました...&gt&ltが多すぎます。さらに、
19:42

追加の小児科については、に置き換えsizeof(T)*8てくださいsizeof(T)*CHAR_BITS
Pillsy 2010

6
@andand追加のペンダントリーを追加するにはsizeof(T)*CHAR_BITstd::numeric_limits<T>::digits(ほぼ4年後のペダントリー)に置き換えます。
Morwenn、2014

1
それはそうCHAR_BITであるべきではありませんCHAR_BITS
Xunie

1
rv =(rv << 1)| (n&0x01);
Vignesh

16

2行:

for(i=0;i<8;i++)
     reversed |= ((original>>i) & 0b1)<<(7-i);

または、「0b1」部分に問題がある場合:

for(i=0;i<8;i++)
     reversed |= ((original>>i) & 1)<<(7-i);

「元の」は、反転したいバイトです。「反転」は結果で、0に初期化されます。


14

おそらく移植性はありませんが、私はアセンブリ言語を使用します。
多くのアセンブリ言語には、ビットをキャリーフラグに回転させ、キャリーフラグをワード(またはバイト)に回転させる命令があります。

アルゴリズムは次のとおりです。

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.

7
この答えは単純なものの反対だと思います。移植性がなく、アセンブリであり、実際のアセンブリではなく疑似コードで記述できるほど複雑です。
deft_code

3
とてもシンプルです。アセンブリニーモニックはプロセッサの種類に固有であり、そこには多くの種類があるので、私はそれを疑似コードに入れました。必要に応じて、これを編集して単純なアセンブリ言語を表示できます。
Thomas Matthews

コンパイラの最適化が適切なアセンブリ命令に簡略化されるかどうかを確認できます。
Sparky

12

次の解決策は、ここで見た他のビット操作アルゴリズムよりも簡単だと思います。

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

綺麗な!これまでの私のお気に入り。
Nick Rameau、

これは確かに単純ですが、実行時間はO(log₂n)ではなくO(n)であることに注意してください。nはビット数(8、16、32、64など)です。
トッドリーマン

10

最も簡単な方法は、ループ内のビット位置を反復処理するために考えられます。

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であり、「s」なし
ljrk

8ビットCHAR_BITを想定charしているのになぜ使用するのですか?
chqrlie

6

一定の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));

5

あなたは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


ここでポッドよりもビットセットはどのように単純ですか?コードを表示するかそうでないか。
ヴィルヘルムテル

実際、コードはビットセットを逆にしてから、元のビットセットに戻すと思います。変更ii!= size(); to ii <size()/ 2; そしてそれはより良い仕事をします=)
Viktor Sehr

(@ viktor-sehrいいえ、そうではありません。revはbsとは異なります)。とにかく、私自身は答えが好きではありません。これは、2項算術演算子とシフト演算子の方が適しているケースだと思います。それでも、理解するのは最も簡単です。
baol、2010

どのようにstd::vector<bool> b = { ... }; std::vector<bool> rb ( b.rbegin(), b.rend()); -逆反復子を直接使用しますか?
MSalters 2017

@MSalters不変性が好きです。
baol

5

「最も簡単な方法」の意味に応じて、ビットを反転する多くの方法があります。


回転で反転

おそらく最も論理的な方法は、最初のビットにマスクを適用しながらバイトを回転させることです(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と同等です。


1行で反転

この解決策は 、プログラミングハックセクションの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;
}

[C ++のみ]署名されていないものを逆にする(テンプレート)

上記のロジックは、あらゆるタイプの符号なしで機能するループで要約できます。

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

asm volatileで反転

最後に重要なことですが、もし最も単純な行数が少ないということであれば、インラインアセンブリを試してみませんか?

-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

3

テーブルルックアップまたは

uint8_t rev_byte(uint8_t x) {
    uint8_t y;
    uint8_t m = 1;
    while (m) {
       y >>= 1;
       if (m&x) {
          y |= 0x80;
       }
       m <<=1;
    }
    return y;
}

編集する

あなたのためにうまくいくかもしれない他の解決策をここで見てください


3

遅いがシンプルな実装:

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

3

これは高速な解決策ですか?

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ループを使用する煩わしさを解消します。しかし、これが効率的で速いかどうか専門家に教えてください?


これの実行時間はO(log₂n)ではなくO(n)です。nはビット数(8、16、32、64など)です。O(log₂n)時間で実行される回答については、他の場所を参照してください。
トッドリーマン

2

アルゴリズムソリューションを実装する前に、使用しているCPUアーキテクチャのアセンブリ言語を確認してください。アーキテクチャには、このようなビット単位の操作を処理する命令が含まれている場合があります(単一のアセンブリ命令よりも簡単なものはありますか?)。

そのような指示が利用できない場合は、ルックアップテーブルルートを使用することをお勧めします。スクリプト/プログラムを記述してテーブルを生成できます。ルックアップ操作は、ここでのビット反転アルゴリズムよりも高速です(ルックアップテーブルをどこかに保存する必要があります)。


2

この単純な関数は、マスクを使用して入力バイトの各ビットをテストし、それをシフト出力に転送します。

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

マスクは署名されていなくて申し訳ありません。
luci88filter

1

これは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囲みます。おそらく、マクロの名前を、より複雑な(実行時の)式にしたくないヒントに変更します。または、インライン関数にします。(しかし、全体的に私はこれが非常に単純で、エラーの可能性がほとんどなく、メモリから簡単に実行できるので
気に入ってい

1

コンパイラがunsigned long longを許可すると仮定します。

unsigned char reverse(unsigned char b) {
  return (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;
}

ここで発見


1

小型のマイクロコントローラーを使用していて、フットプリントの小さい高速ソリューションが必要な場合は、これがソリューションになる可能性があります。Cプロジェクトで使用することは可能ですが、このファイルをアセンブラファイル* .asmとしてCプロジェクトに追加する必要があります。手順:Cプロジェクトで、次の宣言を追加します。

extern uint8_t byte_mirror(uint8_t);

Cからこの関数を呼び出す

byteOutput= byte_mirror(byteInput);

これはコードであり、8051コアにのみ適しています。CPUレジスタには、r0byteInputからのデータです。コードは右に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 <キャリー


このコードは質問に答えることがありますが、それがどのように機能し、いつそれを使用するかを説明するいくつかのコンテキストを含めることをお勧めします。コードのみの回答は、長期的には役に立ちません。
fNek

0
  xor ax,ax
  xor bx,bx
  mov cx,8
  mov al,original_byte!
cycle:   shr al,1
  jnc not_inc
  inc bl
not_inc: test cx,cx
  jz,end_cycle
  shl bl,1
  loop cycle
end_cycle:

逆バイトはblレジスタにあります


3
公正な答えかもしれない他のコンテキストでは、質問はasmではなくCまたはC ++に関するものでした...
jadsq

0
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_t1ビットのフィールドの使用は少し醜いです。なぜなら、最初は8ビットが必要だと言われているようですが、行の終わりで1ビットのみと定義しているためです。私はunsigned b0:1等を使用します
Arkku

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

説明を追加してください。
zcui93

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

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

0

これまでの回答ではこのようなものを見つけることができないので、私は自分の解決策にチップを入れます。多分少しオーバーエンジニアリングされていますが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];
}

https://godbolt.org/z/cSuWhF


0

以下に、シンプルで読みやすいソリューションを示しますsizeof(char) == sizeof(int)

#include <limits.h>

unsigned char reverse(unsigned char c) {
    int shift;
    unsigned char result = 0;

    for (shift = 0; shift < CHAR_BIT; shift++) {
        result <<= 1;
        result |= c & 1;
        c >>= 1;
    }
    return result;
}

0

私はこの質問に日付が付いていることを知っていますが、このトピックはいくつかの目的に関連していると思います。これは非常にうまく機能し、読みやすいバージョンです。これが最も高速または最も効率的であるとは言えませんが、最もクリーンなものの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

0

これは、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;
}

1
この関数は実際には機能しません。0b11001111の逆は0b11110011である必要がありますが、この関数では失敗します。同じテスト方法は、ここにリストされている他の多くの機能で機能します。
ダン

はい、私は私の答えを訂正したおかげで。私の間違いについて知らせてくれてありがとう:)
R1S8K
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.