0と-0を区別することは可能ですか?


94

整数値0-0は基本的に同じであることを知っています。しかし、それらを区別することは可能かどうか疑問に思っています。

たとえば、変数が割り当てられた-0かどうかはどうすればわかりますか?

bool IsNegative(int num)
{
    // How ?
}

int num = -0;
int additinon = 5;

num += (IsNegative(num)) ? -addition : addition;

-0メモリに保存された値はとまったく同じ方法0ですか?


9
整数の場合、違いはありません。
Maroun、2015

14
これは実装に依存しますが、がint2の補数で表される実装(最も一般的に遭遇するもの)で0あり-0、同一のビットごとの表現を持ちます。
Mankarse

11
2の補数マシンでは、ビットレベルでの違いはありません。
Marco A.

17
@VirtualSnake:「バイナリ」の意味は?実際に、-0と0の違いがあるバイナリエンコーディングがあります。たとえば、符号と大きさです。
Benjamin Lindley 2015

8
@VirtualSnakeそうですねintOnesの補数エンコーディングを参照してください。
CiaPan 2015

回答:


112

それはあなたが対象としているマシンに依存します。

整数に2の補数表現を使用するマシンでは、ビットレベル0との間に違いはありません-0(それらは同じ表現です)

マシンが補数を使用している場合、間違いなく

0000 0000   -> signed01111 1111   -> signed   0

明らかに、ネイティブサポートの使用について話しています。x86シリーズプロセッサは、符号付き数値の2の補数表現をネイティブサポートしています。他の表現を使用することは間違いなく可能ですが、おそらく効率が悪く、より多くの指示が必要になります。

(JerryCoffinも述べたように:補数が主に歴史的な理由で考慮されていたとしても、符号付きの大きさの表現は依然としてかなり一般的であり、負のゼロと正のゼロの別々の表現を持っています)


6
@TobiMcNamobi:心配するほどの可能性は低い。このようなマシンの出力を生成するためにC ++コンパイラーを移植することに煩わされた人がいるとしたら、私は驚きます。
ベンジャミンリンドリー2015

1
私はベンジャミンに同意します。歴史的にそれを使用するマシンがありましたが、今日では、それを使用する生産マシンを知りません。それでも、知っておくことと覚えておくことは常に良いことです。
Marco A.

4
@TobiMcNamobi 1の補数は、UNISYS 2200システムで使用中であるstackoverflow.com/a/12277974/995714の stackoverflow.com/q/6971886/995714
phuclv

2
私は1の補数の要件を見たことがありません-ない標準は、実際に保証すること0-0されている違いますか?私は正直に言って、同じ値の2つのビット表現を許可するように動作することを期待していたでしょう。

8
@Hurkly:いいえ、負のゼロ表現が存在する場合でも、標準では、式を使用した代入または初期化-0(つまり、単項演算-子を整数定数に適用した結果)が0負のゼロ表現であることを保証していません。かかわらず、表現の、標準は言うことはありません0し、-0負のゼロビットパターンがあるかもしれないだけで、数学的に異なる値です。あれば、それはまだ同じ数値、0を表す
スティーブ・ジェソップ

14

int(ほとんど普遍的な「2の補数」表現で)の表現0-0同じです。(これらは、IEEE 754浮動小数点など、他の数値表現では異なる場合があります。)


9
>> 2の補数表現を想定
Marco A.

12

2の補数で0を表すことから始めましょう(もちろん、他の多くのシステムと表現が存在します。ここでは、この特定のものを参照します)、8ビット、0と仮定すると、

0000 0000

次に、すべてのビットを反転し、1を追加して2の補数を取得します。

1111 1111 (flip)
0000 0001 (add one)
---------
0000 0000

を得0000 0000ました。これは-0の表現でもあります。

ただし、1の補数では、符号付き0は0000 0000ですが、-0は1111 1111です。


1
私の回答を改善するために反対票を投じる理由を教えてください。
Maroun、2015

1
他のほとんどの答えは技術的に正しいですが、あなたの答えは実用的であり、実装を提供します。良い。
umlcat 2015年

9

CとC ++の実装は通常密接に関連しているため、この回答はそのままにすることにしましたが、実際には、私が思ったようにC標準に準拠していません。重要な点は、C ++標準では、このような場合に何が起こるかを指定していないということです。また、2の補数以外の表現が現実の世界で非常にまれであること、およびそれらが存在する場合でも、誰かが簡単に発見できるものとして公開するのではなく、多くの場合、違いを隠すことがよくあることも重要です。


それらが存在する整数表現の負のゼロの動作は、C標準の場合ほど厳密にC ++標準で定義されていません。ただし、Cレベル(ISO / IEC 9899:1999)を最上位の規範的な参照として引用しています[1.2]。

C標準[6.2.6.2]では、負のゼロは、ビットごとの演算、または負のゼロがすでに存在する演算(たとえば、負のゼロを値で乗算または除算する、または負のゼロをゼロ)-したがって、例のように、通常のゼロの値に単項マイナス演算子を適用すると、通常のゼロになることが保証されます。

負のゼロを生成する可能性がある場合でも、負のゼロをサポートするシステムであっても、それら発生する保証はありません。

これらのケースが実際に負のゼロまたは通常のゼロを生成するかどうか、および負のゼロがオブジェクトに格納されたときに通常のゼロになるかどうかは指定されていません。

したがって、結論付けることができます。いいえ、このケースを検出する信頼できる方法はありません。2の補数以外の表現が現代のコンピュータシステムでは非常にまれであるという事実がなくても。

C ++標準では、「負のゼロ」という用語については触れられておらず、[3.9.1パラ7]が許可されていることに注意することを除いて、符号付きの大きさと1の補数表現の詳細についてはほとんど議論されていません。


一般的にいいえ、Cで何かがtrue / requiredであるという事実は、必ずしもC ++でtrue / requiredであることを意味するわけではありません。Cが規範的な参照であるという事実は、C ++がさまざまなもの(主に標準ヘッダーの内容)についてC標準を参照することを意味しますが、整数型の定義はそれらの1つではありません。ただし、負のゼロを生成する保証された方法がないことは、結論が正しいことを意味します。表現が存在していても、算術を使用してゼロを生成する確実な方法はありません。
スティーブジェソップ2015

では、なぜC ++標準は、このようなことについてあまり詳細に触れないのでしょうか。
Random832

1
C ++標準で投票する人の数を「個人的」と見なすことができるかどうか、個人的な好みだと思います:-)ただし、定義をC標準に任せる場合は、適切に処理できます。他の場合のように、詳細は含まれていませ
スティーブジェソップ2015

「C ++は、ISO / IEC 9899:1999プログラミング言語-C(以下、C標準と呼ぶ)で説明されているCプログラミング言語に基づく汎用プログラミング言語です。」[1.1パラ2]規範的な意味はありますか?これは、C ++標準によって明確にオーバーライドされていないものについて、C標準を組み込むことを意図したものだと思いました。
Random832

@ Random832いいえ。これは単なる歴史的なメモです(たとえば、C ++には初期化子や複合リテラルがない_Bool_Complex、指定されています)。C ++標準は、必要に応じてC標準を組み込む方法を知っています。たとえば、[basic.fundamental] / p3:「符号付きおよび符号なし整数型は、C標準のセクション5.2.4.2.1で与えられた制約を満たさなければなりません。」
TC、2015年

8

あなたのマシンがための明確な表現がある場合-0とし+0、その後、memcmpそれらを区別することができるようになります。

パディングビットが存在する場合、実際にはゼロ以外の値の複数の表現も存在する可能性があります。


5

C ++言語仕様では、負のゼロなどのintはありません。

これらの2つの単語が持つ唯一の意味は、3と5がandに適用される2項演算子と同様-0、に適用される単項演算子です。+35

明確な負のゼロがある場合、2の補数(整数型の最も一般的な表現)は、2つの形式のゼロを表す方法がないため、C ++実装では不十分な表現になります。


対照的に、浮動小数点(IEEEに準拠)には、正と負のゼロが別々にあります。たとえば、1をそれらで割ると、それらを区別できます。正のゼロは正の無限大を生成します。負のゼロは負の無限大を生成します。


ただし、int 0(またはint、またはその他のタイプのその他の値)のメモリ表現が異なる場合は、を使用memcmpして次のことを検出できます。

#include <string>

int main() {
    int a = ...
    int b = ...
    if (memcmp(&a, &b, sizeof(int))) {
        // a and b have different representations in memory
    }
}

もちろん、これが発生した場合、直接メモリ操作以外では、2つの値はまったく同じように機能します。


3
実際、その存在を義務付けていない言語は、その存在を義務付けているわけではありません。ヒント:どちらも義務付けられていません。
Deduplicator

2
@Deduplicator、そうですね。「C ++言語で」とは、「C ++言語仕様で」という意味です。仕様にはフロビネーターについての言及もないので、あいまいさをあまり付けずに「C ++にはフロビネーターがない」と言えるでしょう。はっきりしていると思いましたが、改善していきます。
Paul Draper

1
言語仕様ではユニコーンについても触れられていません。
ypercubeᵀᴹ

2

単純化するために、視覚化する方が簡単だと思いました。

タイプint(_32)は32ビットで格納されます。32ビットは、2 ^ 32 = 4294967296の一意の値を意味します。したがって:

unsigned intのデータ範囲は0〜4,294,967,295です

負の値の場合、それらがどのように格納されるかによって異なります。万一に備えて

以下の場合は1の補数値-0が存在します。


2
私は反対票を投じていませんが、int32ビットに格納されていないプラットフォームは、最近の補完機能を持つプラットフォームよりも人気があります。
Maciej Piechotka
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.