2017-07-06 21 views
-1

編集:私はこの例をC言語に更新しました。特にC++ではなく、C++ではありません(以下の状況を参照してください)。C符号付きを符号なしに維持する正確なビット

私はいつも変換の間の正確な同じビットパターンを維持しながら、符号なし整数への符号付き整数に変換する安全な方法を探しています。私が理解しているように、単にキャストすることは定義されていないか、または実装に依存するため、依存するのは安全ではありません(以下のケースA)。しかし、ORのようなビットワイズ演算子(以下のケースB)はどうでしょうか?ビット単位でORを使用して、符号付きから符号なしに安全に変換できますか?その逆はどうですか?

例:

#include <stdio.h> 

int main() { 
    // NOTE: assuming 32bit ints 
    // example bit pattern: 11111111110001110001001111011010 
    // signed int value: -3730470 
    // unsigned int value: 4291236826 

    // example 1 
    // signed -> unsigned 
    int s1 = -3730470; 
    unsigned int u1a = (unsigned int)s1; 
    unsigned int u1b = (unsigned int)0 | s1; 

    printf("%u\n%u\n", u1a, u1b); 

    // example 2 
    // unsigned -> signed 
    unsigned int u2 = 4291236826; 
    int s2a = (int)u2; 
    int s2b = (int)0 | u2; 

    printf("%i\n%i\n", s2a, s2b); 
} 

状況:私はPOPCOUNT機能(my first attempt code here)を追加するPostgreSQL C-Language function /拡張を書いています。 PostgreSQLは、符号なしタイプ(ref)をサポートしていません。私が見つけたpopcountを計算するすべての効率的な方法は、符号なしデータ型が正しく機能することを要求します。したがって、ビットパターンを変更することなく、符号付きデータ型を符号なしデータ型に変換できなければなりません。オフトピック

私は別の解決策ではなく、整数データ型のPostgreSQLのビット列bitvarbitデータ型を使用することであろうことを認識しますが、私の目的のために整数データ型を使用して管理する方がはるかに簡単です。

+0

正確なビットを維持するために 'union 'を使用してください。 「単純にキャストに未定義または実装に依存する動作があるため、依存しても安全でない」ということは、「符号なし」には向かない。 – chux

+2

コードはC++で表示されますが、質問にはタグが付けられています。 –

+1

符号付きビット表現は実装定義です。したがって、明らかに異なるプラットフォームでは動作が異なります。 –

答えて

2

常にunionintは珍しい非2年代であっても、以下のように動作する変換の間の正確な同一のビットパターン

を維持しながら、符号なし整数に符号付き整数に変換する安全な方法補体。非常に期待しているプラ​​ットフォーム(シリコン墓地にあります)でのみ、INT_MAX == UINT_MAXが問題になります。 1は、一般的な2の補数intに自分自身を制限することができるかどう

union { 
    int i; 
    unsigned u; 
} x = { some_int }; 
printf("%d\n", some_int); 
printf("%u\n", x.u); 

はまだ、以下で十分です。

unsigned u = (unsigned) some_int; 

しかし、どのようなOR(以下、ケースB)のようなビット単位の演算子についてはどうですか?
は、ビット単位のORを安全に無署名に署名変換するために使用することはできますか?

|

以下により整数キャンペーンに隠されたキャストのようなものである:幅によって制限されるようintは、ビットフィールドに対して、(元の型のすべての値を表すことができる場合

)、値はintに変換されます。それ以外の場合はunsigned intに変換されます。 C11dr§6.3.1。1 3

int s1 = -3730470; 
unsigned int u1b = (unsigned int)0 | s1; 
// just like 
       = (unsigned int)0 | (unsigned int)s1; 
       =     (unsigned int)s1; 

何逆はどうですか?

値が両方[0...INT_MAX]で表現可能であればsigned intからunsigned intが明確に定義されている変換します。 ... int

をアウトオブint -range unsignedをされた変換結果は実装定義または実装定義信号が発生されますか。 §6.3.1.33

ビット操作には符号なしタイプを使用することをお勧めします。
以下のコードは期待通りに機能することがありますが、堅牢なコーディングには使用しないでください。

// NOTE: assuming 32bit ints, etc. 
unsigned int u2 = 4291236826; 
int s2a = (int)u2; // avoid this 

代替

int s2a; 
if (u2 > INT_MAX) { 
    // Handle with some other code 
} else { 
    s2a = (int) u2; // OK 
} 

ところで:より良い確か符号なし定数long longような意図とされていないコンパイラに伝えるために4291236826のようにuに符号なしの定数を追加します4291236826.

unsigned int u2 = 4291236826u; 
+0

'int s1 = -3730470のような音です。 unsigned int u1a =(unsigned int)s1; 'は私の目的のために動作します。ありがとう! – Mike

+0

はい - 符号なしの型にするのは難しいことです。 'int'に行くのは、よく避けてください。 – chux

+0

実際、明示的なキャストは必要ですか?私は 'unsigned int u1a = s1;'を行うことができると仮定します。 – Mike

関連する問題