6

例えば:

unsigned int numA = 66; // or anything really
unsigned int numB = -numA;
unsigned int numC = numA & numB

ビットごとの補数演算子を使用して 2 の補数を取得できることを理解しています (+1 と組み合わせて)。

私が尋ねる理由は、チェス エンジンのコードでこれを見つけたからです。チェス エンジンは、絶対的な速度を得るために多くの「ハック」なことを行います。特に、1 秒間に何百万回も呼び出される手番生成関数がそうです。(それが魔法のビットボード移動生成の例であったことは助けにはなりません - それらすべての中で最も最適化されています)。特に、このチェス エンジン コードは、gcc コンパイルでしか正しく動作しません (私はそう思います)。

異なるコンパイラはこれをどのように扱いますか? 特に、VS Studio 2012 Express の C++ コンパイラと比較して、gcc はこれをどのように処理しますか。

ありがとう。

4

4 に答える 4

12

標準からの関連する引用は、実際には次のとおりです。

(§5.3.1/8) 単項 - 演算子のオペランドは、算術またはスコープなしの列挙型を持ち、結果はそのオペランドの否定になります。整数昇格は、整数オペランドまたは列挙オペランドに対して実行されます。符号なし量の負数は、その値を 2 nから減算することによって計算されます。ここで、n はプロモートされたオペランドのビット数です。結果の型は、プロモートされたオペランドの型です。

(これは C++11 のものです。以前のバージョンでは 5.3.1/7 でした。)

-numそのため、2 CHAR_BIT *sizeof(num) - num (‡)として評価されます。結果は (整数昇格後) オペランドと同じ型になります。つまり、符号なしになります。

GCCでテストしたところ、標準で記述されているとおりに操作を実行しているようです。これは、Visual C++ にも当てはまると思います。それ以外の場合はバグです。


(‡)この式は、関連するビット数がメモリ内の変数のサイズ (ビット単位) に対応することを前提としています。Keith Thompson がコメントで指摘しているように、パディング ビットがある場合 (つまり、すべてのビットが数値の表現に関与しているわけではない場合、これは §3.9.1/1 に従って可能です)、これは当てはまりません。数値を表すために使用されるよりも多くのビットを値を格納するために使用するシステムでは、式は正確ではありません。(個人的には、そのようなシステムを実際に認識していません。)

于 2012-12-28T06:12:19.820 に答える
3

セクション 4.7.2 (整数変換) で C++ 標準が述べていることは次のとおりです。

宛先の型が符号なしの場合、結果の値は、ソースの整数と一致する最小の符号なし整数になります (モジュロ 2 nで、n は符号なしの型を表すために使用されるビット数です)。[ 注: 2 の補数表現では、この変換は概念的なものであり、ビット パターンに変更はありません (切り捨てがない場合)。—終わりのメモ]

これがあなたの質問に答えることを願っています。

于 2012-12-28T06:10:13.920 に答える
2

あなたはCとC++の両方について尋ねました。それらは2つの異なる言語であることを忘れないでください。この特定のケースでは、符号なし型の操作について同じルールがありますが、表現が異なります。

現在の(2011)ISO C標準の最新ドラフトを引用)、セクション6.2.5p9:

結果の符号なし整数型で表現できない結果は、結果の型で表現できる最大値より1大きい数を法として減少するため、符号なしオペランドを含む計算はオーバーフローすることはありません。

単項「-」演算子の説明は、結果が「その(プロモートされた)オペランドの負」であることを単に示しています。読者が6.2.5を読んで、符号なし整数の「負」が何であるかを理解していることを前提としています。

どちらの言語でも、次の結果が得られます。

unsigned int numA = 66;
unsigned int numB = -numA;

に保存UINT_MAX - 66U + 1UすることnumBです。(U接尾辞は実際には必要ありませんが、これはすべて符号なしの値で定義されていることを強調するために含めます。)

于 2012-12-30T05:39:03.637 に答える