2

コンパイル時にパリティを生成したいとします。パリティ計算にはリテラル定数が与えられ、適切なオプティマイザーを使用すると、それ自体が単一の定数に要約されます。次に、 Cプリプロセッサを使用した次のパリティ計算を見てください。

#define PARITY16(u16) (PARITY8((u16)&0xff) ^ PARITY8((u16)>>8))
#define PARITY8(u8) (PARITY4((u8)&0x0f) ^ PARITY4((u8)>>4))
#define PARITY4(u4) (PARITY2((u4)&0x03) ^ PARITY2((u4)>>2))
#define PARITY2(u2) (PARITY1((u2)&0x01) ^ PARITY1((u2)>>1))
#define PARITY1(u1) (u1)

int message[] = { 0x1234, 0x5678, PARITY16(0x1234^0x5678));

これにより、コンパイル時にパリティが計算されますが、大量の中間コードが生成され、式の16個のインスタンスに拡張されます。これは、u16たとえば任意の複雑な式にすることができます。問題は、Cプリプロセッサが中間式を評価できず、一般的な場合はテキストのみを展開することです(整数演算をその場で実行するように強制できますが、些細な場合、またはギガバイトの#definesを使用する場合のみ)。3ビットのパリティは、算術式によって一度に生成できることがわかりました([0..7]*3+1)/4。これにより、16ビットのパリティが次のマクロに削減されます。

#define PARITY16(u16) ((4 & ((((u16)&7)*3+1) ^           \
                            ((((u16)>>3)&7)*3+1) ^               \
                            ((((u16)>>6)&7)*3+1) ^               \
                            ((((u16)>>9)&7)*3+1) ^               \
                            ((((u16)>>12)&7)*3+1) ^              \
                            ((((u16)>>15)&1)*3+1))) >> 2))

u16これは6倍しか拡大しません。(拡張の数に関して)さらに安価な方法はありますか?たとえば、4,5などの直接式はありますか?ビットパリティ?(x*k+d)/m3ビットを超える範囲の許容可能な(オーバーフローしない)値k、d、mの形式の線形式の解を見つけることができませんでした。プリプロセッサのパリティ計算のためのより賢いショートカットを持っている人はいますか?

4

2 に答える 2

1

あなたが探しているのはこのようなものですか?次の「PARITY16(u16)」プリプロセッサマクロは、構造体の割り当てでリテラル定数として使用でき、引数を1回だけ評価します。

/* parity.c
 * test code to test out bit-twiddling cleverness
 * 2013-05-12: David Cary started.
*/

// works for all 0...0xFFFF
// and only evalutes u16 one time.
#define PARITYodd33(u33) \
    ( \
        ((((((((((((((( \
            (u33) \
        &0x555555555)*5)>>2) \
        &0x111111111)*0x11)>>4) \
        &0x101010101)*0x101)>>8) \
        &0x100010001)*0x10001)>>16) \
        &0x100000001)*0x100000001)>>32) \
    &1)
#define PARITY16(u16) PARITYodd33(((unsigned long long)u16)*0x20001)

// works for all 0...0xFFFF
// but, alas, generates 16 instances of u16.
#define PARITY_16(u16) (PARITY8((u16)&0xff) ^ PARITY8((u16)>>8))
#define PARITY8(u8) (PARITY4((u8)&0x0f) ^ PARITY4((u8)>>4))
#define PARITY4(u4) (PARITY2((u4)&0x03) ^ PARITY2((u4)>>2))
#define PARITY2(u2) (PARITY1((u2)&0x01) ^ PARITY1((u2)>>1))
#define PARITY1(u1) (u1)

int message1[] = { 0x1234, 0x5678, PARITY16(0x1234^0x5678) };
int message2[] = { 0x1234, 0x5678, PARITY_16(0x1234^0x5678) };

#include <stdio.h>

int main(void){
        int errors = 0;
        int i=0;
        printf(" Testing parity ...\n");
        printf(" 0x%x = message with PARITY16\n", message1[2] );
        printf(" 0x%x = message with PARITY_16\n", message2[2] );
        for(i=0; i<0x10000; i++){
                int left = PARITY_16(i);
                int right =  PARITY16(i);
                if( left != right ){
                        printf(" 0x%x: (%d != %d)\n", i, left, right );
                        errors++;
                        return 0;
                };
        };
        printf(" 0x%x errors detected. \n", errors );
} /* vim: set shiftwidth=4 expandtab ignorecase : */

投稿した元のコードと同じように、ビットをペアにして(実際には)各ペア間のXORを計算し、その結果から再びビットをペアにして、パリティビットが1つだけ残るまで毎回ビット数を半分にします。

しかし、それは本当にあなたが望んでいたことですか?

多くの人が、メッセージの「パリティ」を計算していると言います。しかし、私の経験では、ほとんどの場合、実際には単一のパリティビットよりも大きいエラー検出コード( LRCCRCハミングコードなど)を生成しています。

詳細

現在のシステムが妥当な時間でコンパイルされていて、正しい答えが得られている場合は、そのままにしておきます。「プリプロセッサが定数を生成する方法」をリファクタリングすると、ビットごとに同じランタイム実行可能ファイルが生成されます。コンパイルに1秒長くかかる場合でも、読みやすいソースが欲しいです。

多くの人は、標準のCプリプロセッサよりも読みやすい言語を使用してCソースコードを生成します。pycrc文字セットエクストラクタ「Pythonを使用してCを生成する」などを参照してください。

現在のシステムがCプリプロセッサを微調整するのではなく、コンパイルに時間がかかりすぎる場合は、パリティを含むそのメッセージを、ハードコードされた定数を含む別の「.h」ファイルに入れたいと思うでしょう(強制するのではなく)毎回それらを計算するCプリプロセッサ)、および組み込みシステムの「.c」ファイルにその「.h」ファイルを「#include」します。

次に、パリティ計算を実行し、その「.h」ファイルの内容を事前に計算されたCソースコードとして出力する完全に別個のプログラム(おそらくCまたはPython)を作成します。

print("int message[] = { 0x%x, 0x%x, 0x%x };\n",
    M[0], M[1], parity( M[0]^M[1] ) );

そして、MAKEFILEを微調整して、そのPython(またはその他の)プログラムを実行し、必要な場合にのみ、その「.h」ファイルを再生成します。

于 2013-05-13T05:43:59.013 に答える
-1

mfontaniniが言うように、インライン関数の方がはるかに優れています。

マクロを主張する場合は、一時変数を定義できます。
を使用gccすると、それを実行しても、式として動作するマクロを使用でき
#define PARITY(x) ({int tmp=x; PARITY16(tmp);})
ます。標準に固執する場合は、マクロをステートメントにする必要があります
#define PARITY(x, target) do { int tmp=x; target=PARITY16(tmp); } while(0)

どちらの場合もtmp、関数で使用される名前になってしまうと、醜いバグが発生する可能性があります(さらに悪いことに、マクロに渡されるパラメーター内で使用されます)。

于 2012-07-02T13:27:14.820 に答える