符号付きの数値に対して、符号と絶対値よりも2の補数を好むのはなぜですか?


201

-1を2進数で表すために、2の補数が使用される理由があるかどうか知りたいだけです。ビットを反転して1を追加しますか?

-1は、10000001ではなく11111111(2の補数)で表されます。これは、最初のビットが負のフラグであるバイナリ1である10000001です。

免責事項:私は自分の仕事のためにバイナリ算術に依存していません!


6
FWIW、サインビットを使用する「直感的な」方法は、時々使用されます。たとえば、ほとんどのコンピューターは、浮動小数点数を表すときにサインビットを使用します。
Adisak、

2
@Adisakこれは符号付きマグニチュードと呼ばれます
Cole Johnson

2
浮動小数点数には、符号、指数、および仮数(多くの場合、暗黙的な「1」)の3つのコンポーネントが含まれているため、常に符号と大きさの表現を整数に関連付けています。しかし、厳密に線形ではないことがわかっている限り、指数と仮数をマグニチュードとして扱うのは簡単だと思います。
Adisak 2013

@Adisakの発言に興味がある人のために、浮動小数点数が2進数でどのように格納されるかを説明する記事を次に示します。
GDP2 2017

回答:


333

これは、加算が負の数を処理するための特別なロジックを必要としないように行われます。ウィキペディアの記事をチェックしてください

たとえば、2と-1の2つの数値があるとします。数値を表す「直感的な」方法では、数値はそれぞれ00101001になります(サイズは4ビットに固執しています)。では2の補数の方法、彼らはある00101111。今、それらを追加したいとしましょう。

2の補数の加算は非常に簡単です。通常は数字を追加し、最後のキャリービットは破棄されます。したがって、次のように追加されます。

  0010
+ 1111
=10001
= 0001 (discard the carry)

0001 「2 +(-1)」の予想結果である1です。

しかし、「直感的な」方法では、追加はより複雑です。

  0010
+ 1001
= 1011

-3はどれですか?この場合、単純な追加は機能しません。数値の1つが負であり、そうである場合は別のアルゴリズムを使用することに注意する必要があります。

この「直感的な」保存方法の場合、減算は加算とは異なる演算であり、加算する前に数値をさらにチェックする必要があります。最も基本的な演算(加算、減算など)をできるだけ高速にしたいので、できるだけ単純なアルゴリズムを使用できるように数値を格納する必要があります。

さらに、「直感的な」保存方法では、2つのゼロがあります。

0000  "zero"
1000  "negative zero"

直感的には同じ数ですが、保存時に2つの異なる値があります。すべてのアプリケーションは、ゼロ以外の値も負のゼロにならないようにするために、追加の手順を実行する必要があります。

この方法でintを格納することにはもう1つのボーナスがあります。それは、レジスタの幅を拡張する必要がある場合です。値は格納されます。2の補数を使用すると、4ビットの数値を8ビットのレジスタに格納することは、その繰り返しです。上位ビット:

    0001 (one, in four bits)
00000001 (one, in eight bits)
    1110 (negative two, in four bits)
11111110 (negative two, in eight bits)

それは、小さな単語の符号ビットを見て、それが大きな単語の幅を埋めるまで繰り返すだけです。

このメソッドでは、既存のビットをクリアする必要があります。これは、パディングに加えて追加の操作です。

    0001 (one, in four bits)
00000001 (one, in eight bits)
    1010 (negative two, in four bits)
10000010 (negative two, in eight bits)

どちらの場合も、これらの追加の4ビットを設定する必要がありますが、「直感的」な場合は、5番目のビットもクリアする必要があります。これは、すべてのアプリケーションに存在する最も基本的で一般的な操作の1つの小さな追加手順です。


13
同意する。2の補数が機能します。しかし、そもそもどうやってそれに到達したのでしょうか。この表記に到達する必要があるとしたら、思考プロセスはどうなるでしょうか。2の補数にたどり着くのは運だけじゃないと思いますね。
Lazer、2010

1
また、なぜフロートの2の補数対応がないのですか?
Lazer、2010

6
@Lazerは、この記事をチェックしてhow we arrived at 2s compliment the first place. cs.cornell.edu/~tomf/notes/cps104/twoscomp.html
Ankit

1
Javaは私が知る限り符号付き整数型しか持っていないので、常に2の補数解釈で扱います。他の言語では、値がどのように処理されるかは、コードがそれをどのように処理するかによって異なります。メモリのブロックが符号付きまたは符号なし整数、倍精度浮動小数点数型、文字列などであることを伝えるものは何もありません。生データは、解釈するために選択したタイプです。
Welbog 2015

3
@Suraj、私は完全な答えのために2の補数に関するウィキペディアの記事を見ることをお勧めします:en.wikipedia.org/wiki/Two%27s_complement。短い答えはMSBで1示し-8、そして残りの3つの1Sが示し42および1ので、それぞれ-8+4+2+1 = -1
ウェルボグ2016

18

ウィキペディアはそれをすべて言います:

2の補数システムには、加算および減算回路がオペランドの符号を調べて加算するか減算するかを決定する必要がないという利点があります。この特性により、システムの実装が簡単になり、高精度の演算を簡単に処理できるようになります。また、ゼロは単一の表現のみを持ち、1の補体系に存在する負のゼロに関連する微妙な問題を回避します。

つまり、加算は同じですが、数値が負かどうかは関係ありません。


サー、私がchar a = 12と書いた場合; そしてunsigned char b = 12、基になるビットパターンは同じですか、実際にはどうなりますか?
Suraj Jain、2016

書き込みまたは読み取り時に何も変更されません。加算または減算の場合にのみ適用されます。
Talespin_Kit 2017年

12

この質問は古いですが、2セント入れてみましょう。

これを説明する前に、基本に戻りましょう。2 '補数は、1の補数+ 1です。さて、1の補数とは何か、さらにその意味は何ですか。

任意のnビット数とその1の補数の合計により、それらのnビットで表すことができる最大の数が得られます。例:

 0010 (2 in 4 bit system)
+1101 (1's complement of 2)
___________________________
 1111  (the highest number that we can represent by 4 bits)

結果にさらに1を追加しようとするとどうなるでしょうか。オーバーフローが発生します。

結果1 0000は0になります(4ビットの数値で作業しているため、左側の1はオーバーフローです)。

そう 、

Any n-bit number + its 1's complement = max n-bit number
Any n-bit number + its 1'complement + 1 = 0 ( as explained above, overflow will occur as we are adding 1 to max n-bit number)

次に誰かが1の補数+ 1を2の補数と呼ぶことにしました。したがって、上記のステートメントは次のようになります。任意のnビットの数値+その2の補数= 0は、ある数値の2の補数=-(その数の)

これにより、もう1つの質問が生じます。なぜnビットの(n-1)だけを使用して正の数を表すことができ、なぜ左端のn番目のビットが符号を表すのですか(左端のビットの0は+ ve数を意味し、1は-ve番号)。たとえば、32番目のビットが1である場合、Javaのintの最初の31ビットのみを使用して正の数を表すのはなぜですか。

 1100 (lets assume 12 in 4 bit system)
+0100(2's complement of 12)
___________________________

1 0000(結果はゼロ、キャリー1はオーバーフロー)

したがって、(n + 2'nの補数)= 0のシステムはまだ機能します。ここでの唯一の曖昧さは、2の12の補数が0100であることです。これは、2の補数システムで-12を表すことを除いて、+8も曖昧に表します。

この問題は、正の数の左端のビットが常に0である場合に解決されます。その場合、2の補数は常に左端のビットに1があり、2の補数と+ ve数を表す同じビットのセットのあいまいさはありません。


1
+1しました。それは情報でしたが、結局のところ、それが正の数であるか負の数であるかを表すために最上位ビットのアプローチが必要である理由がわかりません。0は2つの表現-0000(+)と1000(-)を持つような多くの問題があります。また、同じアルゴリズムを使用して加算と減算を行うことはできません。通常の0100と言うと
+8、2

8

2の補数を使用すると、通常の方法で加算と減算を行うことができます(符号なしの数値で負傷したような)。また、-0を防止します(通常のビットごとの数値比較方法では0と等しくない0を表す別の方法)。


6

これは、数値の合計と差を単純化するためです。2の補数で成文化された負の数と正の数の合計は、通常の方法でそれらを合計するのと同じです。


5

この操作の通常の実装は「ビットを反転して1を足す」ですが、おそらく論理的根拠をより明確にする、それを定義する別の方法があります。2の補数は、各ビットが2の次の累乗を制御する通常の符号なし表現を取り、最も重要な項を負にした場合に得られる形式です。

8ビット値を取るa 7 a 6 a 5 a 4 a 3 a 2 a 1 a 0

通常の符号なしバイナリ解釈は次のとおりです:
2 7 * a 7 + 2 6 * a 6 + 2 5 * a 5 + 2 4 * a 4 + 2 3 * a 3 + 2 2 * a 2 + 2 1 * a 1 + 2 0 * a 0
11111111 = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255

2の補数の解釈は次のとおりです。
-2 7 * a 7 + 2 6 * a 6 + 2 5 * a 5 + 2 4 * a 4 + 2 3 * a 3 + 2 2 * a 2 + 2 1 * a 1 + 2 0 * a 0
11111111 = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = -1

他のビットの意味はまったく変化せず、7へのキャリーは「オーバーフロー」であり、機能するとは想定されていないため、ほとんどすべての算術演算は変更なしで機能します(他の人が指摘したとおり)。符号の大きさは、通常、符号ビットを検査し、異なるロジックを使用します。


4

2の補数により、特別なロジックなしで、負の数と正の数を加算できます。

メソッド
10000001(-1)
+00000001(1)
を使用して1と-1を追加しようとすると、
10000010(-2)になります。

代わりに、2の補数を使用して、

11111111(-1)
+00000001(1)
00000000(0)

減算についても同様です。

また、6から4を減算しようとすると(2つの正の数)、2の補数4と2を加算できます6 +(-4)= 6-4 = 2

つまり、正数と負数の両方の減算と加算はすべて、CPU内の同じ回路で実行できます。


4

他の回答を拡張するには:

2の補数で

  • 加算は、単純な正の整数の加算と同じメカニズムです。
  • 減算も変わらない
  • 掛け算も!

除算には別のメカニズムが必要です。

2の補数は通常のモジュラー算術であるため、これらすべてが真です。


拡大しない乗算だけが同じというわけでありません。しかし、ほとんどの高水準言語は明示的なキャストなしで拡大乗算をサポートしていないため、結果はそれらの言語で同じになります。
phuclv 2015年

@LưuVĩnhPhúc:拡大乗算は通常同じですが、符号付きと符号なしの乗算の結果は、結果がsigned intの範囲に収まる場合にのみ同じであることが保証されます。gccのようないくつかのコンパイラ、のような特定の何かunsigned mul(unsigned short x, unsigned short y) { return x*y; }[16ビット短いです。32ビットのint]は時折製品が2147483647より大きい場合に誤動作するコードを生成する
supercat

2

この質問の答えを読んで、私はこのコメントに出くわしました[編集]。

0100(4)の2の補数は1100になります。普通なら、1100は12になります。それで、私が通常の1100と言うと12ですが、2の補数1100と言うと-4ですか?また、Javaで1100(今のところ4ビットを想定)が格納されている場合、+ 12または-4であるかどうかはどのように判断されますか?– hagrawal 7月2日16:53

私の意見では、このコメントで尋ねられた質問は非常に興味深いので、私はまずそれを言い換えて、次に回答と例を提供したいと思います。

質問–システムは、隣接する1つ以上のバイトをどのように解釈する必要があるかをどのように確立できますか?特に、システムは、指定されたバイトのシーケンスがプレーンな2進数か2の補数かをどのようにして確立できますか?

回答–システムは、タイプを介してバイトのシーケンスを解釈する方法を確立します。タイプが定義する

  • 考慮しなければならないバイト数
  • それらのバイトをどのように解釈する必要があるか

例–以下では、

  • charは1バイト長です
  • shortは2バイト長です
  • intfloatは4バイト長です

これらのサイズは私のシステムに固有であることに注意してください。かなり一般的ですが、システムによって異なる場合があります。システムに何があるか知りたい場合は、sizeof演算子を使用してください。

まず最初に、4バイトを含む配列を定義し、それらすべてを1011110116進数に対応する2進数に初期化しますBD

// BD(hexadecimal) = 10111101 (binary)
unsigned char   l_Just4Bytes[ 4 ]   =   { 0xBD, 0xBD, 0xBD, 0xBD };

次に、さまざまなタイプを使用して配列の内容を読み取ります。

unsigned char そして signed char

// 10111101 as a PLAIN BINARY number equals 189
printf( "l_Just4Bytes as unsigned char  -> %hi\n", *( ( unsigned char* )l_Just4Bytes ) );

// 10111101 as a 2'S COMPLEMENT number equals -67
printf( "l_Just4Bytes as signed char    -> %i\n", *( ( signed char* )l_Just4Bytes ) );

unsigned short そして short

// 1011110110111101 as a PLAIN BINARY number equals 48573
printf( "l_Just4Bytes as unsigned short -> %hu\n", *( ( unsigned short* )l_Just4Bytes ) );

// 1011110110111101 as a 2'S COMPLEMENT number equals -16963
printf( "l_Just4Bytes as short          -> %hi\n", *( ( short* )l_Just4Bytes ) );

unsigned intintおよびfloat

// 10111101101111011011110110111101 as a PLAIN BINARY number equals 3183328701
printf( "l_Just4Bytes as unsigned int   -> %u\n", *( ( unsigned int* )l_Just4Bytes ) );

// 10111101101111011011110110111101 as a 2'S COMPLEMENT number equals -1111638595
printf( "l_Just4Bytes as int            -> %i\n", *( ( int* )l_Just4Bytes ) );

// 10111101101111011011110110111101 as a IEEE 754 SINGLE-PRECISION number equals -0.092647
printf( "l_Just4Bytes as float          -> %f\n", *( ( float* )l_Just4Bytes ) );

RAM(l_Just4Bytes[ 0..3 ])の4バイトは常にまったく同じです。変更されるのは、それらの解釈方法だけです。

繰り返しますが、タイプを通じてそれらを解釈する方法をシステム伝えます

たとえば、上記ではl_Just4Bytes配列の内容を解釈するために次のタイプを使用しました

  • unsigned char:プレーンバイナリの1バイト
  • signed char:2の補数で1バイト
  • unsigned short:プレーンバイナリ表記の2バイト
  • short:2の補数の2バイト
  • unsigned int:プレーンバイナリ表記の4バイト
  • int:2の補数の4バイト
  • float:IEEE 754単精度表記の4バイト

[編集]この投稿は、user4581301によるコメントの後に編集されました。いくつかの役立つ行を削除する時間を割いていただきありがとうございます!


そのコードblobは編集が必要なため、読者はスクロールし続ける必要はありません。上部の大量のコメントはプレーンな古いテキストになり、レンダラーがフォーマットを処理するようになります。サイズが固定されていないため、サイズとフォーマットについて説明する最後の方のビットにも警告を追加する必要があります。
user4581301 2015

+1。@ mw215は、この質問と回答のペアをコミュニティWikiのエントリとして独自に作成することを検討します。これは、2の補数の数学以外の生のバイト解釈に興味がある可能性のある人にとって役立つからです。
ウェルボグ2015

私は知りたいだけです2の補数は常に続きます、つまり私が持っているかどうかint x = -4、そしてprintf("%d" , x)どのように解釈されるのですか?また、unsigned intand signed int%dandの違いは何%uですか...これは長い間私を悩ませてきました。ありがとう。
Suraj Jain

@Suraj Jain intタイプを使用する場合、signed修飾子はデフォルトです。つまり、intsigned intはまったく同じタイプです。したがって、2つの定義int i = -4;signed int i = -4;同じ意味を持っています。
mw215 2016

@Suraj Jainシステムは、型を介して一連のバイトを解釈する方法を確立します。タイプは次のことを定義します。考慮しなければならないバイト数とそれらのバイトをどのように解釈する必要があるか。int4つのバイトで2の補数unsigned intの4バイトであるプレーンバイナリ表記(使用して、システム上の実際の型のサイズを確認sizeof演算子)。
mw215 2016

1

スタンフォード大学のYouTubeチャンネルで視聴できるプログラミングパラダイムと呼ばれる一連の講義の2番目の講義(2の補数に関する説明は13:00頃に始まります)で、スタンフォード大学のジェリーケイン教授が2の補数を説明するのを見ることができます。レクチャーシリーズへのリンクは次のとおりです:http : //www.youtube.com/view_play_list?p=9D558D49CA734A02


0

回路に実装するのが簡単で、負のゼロを許容しないため、2の補数が使用されます。

xビットがある場合、2の補数は+(2 ^ x / 2 + 1)から-(2 ^ x / 2)の範囲になります。1の補数は+(2 ^ x / 2)から-(2 ^ x / 2)まで実行されますが、負のゼロを許可します(0000は4ビット1の補数システムでは1000に等しい)。


0

まあ、あなたの意図は本当にあなたの2進数のすべてのビットを逆にすることではありません。実際には、1から各桁を減算することです。1から1を減算すると0になり、0から1を減算すると1になるのは、単なる幸運な偶然です。

しかし、なぜ各桁の1との違いを見つけるのですか?まあ、そうではありません。実際の目的は、指定された2進数と、桁数が同じで1のみを含む別の2進数との差を計算することです。たとえば、数値が10110001の場合、これらのビットをすべて反転すると、効果的に計算されます(11111111-10110001)。

これは、2の補数の計算の最初のステップを説明しています。次に、2番目のステップ(1を追加)も図に含めます。

上記の二項方程式に1を追加します。

11111111-10110001 + 1

何を手に入れますか?この:

100000000〜10110001

これが最終的な方程式です。そして、これらの2つのステップを実行することで、これを見つけようとしています。最終的な違いは、2進数をもう1つの2進数から1桁余分に減算し、最も有意なビット位置を除いてゼロを含むことです。

しかし、なぜこの違いの後に私たちは本当にハンカーしているのですか?さて、これからは、ウィキペディアの記事を読んだ方がいいと思います。


0

足し算、引き算ともに足し算演算のみ行います。追加のために、第1オペランドに第2オペランドを追加します。減算では、第2オペランドの2の補数を第1オペランドに追加します。

2の補数表現では、減算に別個のデジタルコンポーネントは必要ありません。加算器と補数のみが使用されます。


0

一部の初期の追加マシンでは、デジタルコンピューターの時代の前に、各キーに異なる色の凡例のセットを使用してオペレーターに値を入力させることで減算が実行されることに注意してください(したがって、各キーは9から数字を差し引いて減算)、および特別なボタンを押すと、計算への繰り越しが想定されます。したがって、6桁のマシンでは、値から1234を引くために、オペレーターは通常「998,765」を示すキーを押し、ボタンを押してその値に1を加えたものを進行中の計算に追加します。2の補数演算は、単に以前の「10の補数」演算と同等のバイナリです。


0

補数法で減算を実行する利点は、ハードウェアの
複雑さが軽減されることです。加算と減算に別のデジタル回路を使用する必要がありません。加算と減算の両方が加算器のみで実行されます。


0

ここでまだ言及されていない2の補数表現の主な利点は、2の補数の合計、差、または積の下位ビットが、対応するオペランドのビットにのみ依存することです。-1の8ビット符号付き値である理由は、最下位8ビットが他の整数から最下位8ビットである整数を11111111減算すると、最下位8ビットが00000001000000011111111です。数学的には、値-1は1の無限文字列になりますが、特定の整数型の範囲内のすべての値は、特定のポイントを過ぎてすべて1またはすべて0になるため、コンピュータで「符号拡張」するのが便利です。 1または0の無限数を表すかのように、数値の最上位ビット。

2の補数は、バイナリマシンの自然なワードサイズよりも大きい型を処理する場合に適切に機能する唯一の符号付き数表現です。加算または減算を実行するとき、コードは各オペランドの最小チャンクをフェッチし、最小チャンクを計算できるためです。結果を保存してから、各オペランドの次のチャンクをロードし、結果の次のチャンクを計算して保存します。したがって、すべての加算と減算が単一の8ビットレジスタを通過する必要があるプロセッサでも32ビットの符号付き数値をかなり効率的に処理できます(もちろん、32ビットのレジスタを使用するよりも低速ですが、引き続き機能します)。

C規格で許可されている他の符号付き表現を使用する場合、結果のすべてのビットがオペランドの任意のビットの影響を受ける可能性があるため、値全体を一度にレジスターに保持するか、追加の計算を行う必要があります。少なくとも一部のケースでは、結果の各チャンクを読み取り、変更、および再書き込みする必要があるステップ。


段落で回答をフォーマットして、コードをコードとしてマークしてください。読みやすくなり、賛成票が入ります。
Suraj Jain、2016

@SurajJain:いいですか?
スーパーキャット2016

ええ、以前のものよりも優れています。signedchar a = 1とunsigned char a = 1の違いは何ですか、それらはメモリでどのように表されますか。
Suraj Jain

@SurajJain:「チャー」が「INT」よりも小さい2の補数システム[システムの、すなわち大多数]で、符号付きおよび符号なしchar型は同じように動作する以外は、その署名されたタイプは、符号拡張読み取りおよび符号なしのタイプであろうしません。このようなシステムでは、値194または-62を符号付きcharに格納すると、194または-62を符号なしchar(つまり11000010)に格納する場合と同じビットパターンが書き込まれます。-62をもたらすsigned char型からそのビットパターンを読み取り、unsigned char型からの読み取りは194もたらす
supercat

符号拡張された意味?
Suraj Jain、

0

次のようなさまざまなタイプの表現があります。

  1. 符号なし数値表現
  2. 符号付き数値表現
  3. 補数表現
  4. 2の補数表現

-正の数値のみを表すために使用される符号なしの数値表現

-正の数と負の数を表すために使用される符号付き数値表現。符号付き数値表現では、MSBビットは符号ビットを表し、残りのビットは数値を表します。MSBが0の場合、数値は正であり、MSBが1の場合、数値は負です。

符号付き数値表現の問題は、0の値が2つあることです。

補数表現の問題は、0の値が2つあることです。

しかし、2の補数表現を使用する場合、0の値は1つだけであるため、2の補数形式で負の数を表します。

出典:負の数がバイトのギガバイトの2の補数形式で格納される理由


-1

Two2の補数が1の補数システムではなく負の数値を表すために使用される理由の1つの満足のいく答えは、Twoの補数システムが0複数の表現の問題とエンドアラウンドキャリーの必要性を解決することです。負を表す1の補数システムに存在する番号。

詳細については、訪問 https://en.wikipedia.org/wiki/Signed_number_representationsにアクセスしてください。

End-around-carryについては、 https://en.wikipedia.org/wiki/End-around_carryにアクセスしてください


実際には、小数点があり、すべてのビットが何であるかが明示されている場合、「0..0000.1111..1」は、左端の記述されていないビットはすべて0であり、右端の記述されていないビットはすべて1であるため、 「..1」は、キャリーがトリガーされることを意味します。したがって、それは(機械的に) "0.0001.0000..0"です。これは、「1..1111.1111..1」がゼロに等しいことを意味します!これは、整数を無効にするために、実際にはビットを反転するだけであることも意味します。しかし、現在は表現可能な分数に適用されます。
Rob
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.