web-dev-qa-db-ja.com

符号付き数値の符号と大きさよりも2の補数を好むのはなぜですか?

-1を2進数で表すために2の補数が使用される理由がある場合、私は好奇心が強いです:ビットを反転し、1を追加しますか?

-1は、(私にとってより直感的な)10000001ではなく11111111(2の補数)で表されます。

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

190
Ray

負の数を処理するために特別なロジックを追加する必要がないように行われました。 Wikipediaの記事 をご覧ください。

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

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

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

0001は1です。これは、「2 +(-1)」の予期される結果です。

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

  0010
+ 1001
= 1011

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

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

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

0000  "zero"
1000  "negative zero"

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

この方法でintを保存することには別のボーナスがあります。それは、値を保存するレジスタの幅を拡張する必要がある場合です。上位ビット:

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

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

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

    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つにおける小さな追加ステップです。

317
Welbog

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

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

言い換えれば、加算は同じであり、数値が負かどうかに関係ありません。

18
Yacoby

この質問は古いものですが、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 number)。たとえば、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を表す以外に、2の補数の12が0100であることです。

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

12
Rpant

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

8
Zifre

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

6
Stefano Verna

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

8ビット値をとる7 a6 a5 a4 a3 a2 a1 a

通常の符号なしバイナリ解釈は次のとおりです。
27* a7 + 26* a6 + 25* a5 + 24* a4 + 23* a3 + 22* a2 + 21* a1 + 2* a
11111111 = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255

2の補数の解釈は次のとおりです。
-27* a7 + 26* a6 + 25* a5 + 24* a4 + 23* a3 + 22* a2 + 21* a1 + 2* a
11111111 = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = -1

他のビットはどれも意味をまったく変えず、7 は「オーバーフロー」であり、動作することは期待されていないため、ほとんどすべての算術演算は修正なしで動作します(他の人が述べているように)。通常、符号の大きさは符号ビットを検査し、異なるロジックを使用します。

5
puetzk

他の回答を展開するには:

2の補数で

  • 追加は、単純な正の整数の追加と同じメカニズムです。
  • 減算も変わりません
  • 乗算も!

分割には別のメカニズムが必要です。

2の補数は通常のモジュラー算術であり、モジュロを減算することによっていくつかの数値を負として見ることを選択するため、これらはすべて真です。

4
yairchu

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

メソッドを使用して1と-1を追加しようとした場合
10000001(-1)
+ 00000001(1)

10000010(-2)

代わりに、2の補数を使用することにより、追加できます

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

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

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

これは、CPU内の同じ回路で正数と負数の両方の減算と加算をすべて実行できることを意味します。

4

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

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 operator を使用します。

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

// 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バイトは常に正確に同じままです。変更されるのは、それらをどのように解釈するかだけです。

繰り返しますが、weは、システムにhowtypes

たとえば、上記では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によるコメントの後に編集されました。これらのいくつかの有用な行を削除するために時間を割いていただきありがとうございます!

2
mw215

スタンフォード大学のJerry Cain教授が、2つの補数について説明しているのを見ることができます。2番目の補数(2の補数に関する説明は、13時頃に開始されます)。講義シリーズへのリンクは次のとおりです。 http://www.youtube.com/view_play_list?p=9D558D49CA734A02

1
alexs

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

2の補数は、加算または減算を実行するときにコードが各オペランドの最下位チャンクをフェッチし、結果を保存してから、各オペランドの次のチャンクをロードし、結果の次のチャンクを計算して保存します。したがって、すべての加算と減算が単一の8ビットレジスタを通過する必要があるプロセッサでさえ32ビットの符号付き数値を合理的に効率的に処理できます(もちろん、32ビットのレジスタを使用する場合よりも低速ですが、実行可能です)。

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

0
supercat

加算と減算の両方に対して加算演算のみを実行します。追加のために、第1オペランドに第2オペランドを追加します。減算では、第2オペランドの2の補数を第1オペランドに追加します。

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

0
subhakar

まあ、あなたの意図は実際にはあなたの2進数のすべてのビットを逆にすることではありません。実際には、1から各桁を減算します。1から1を減算すると0になり、1から0を減算すると1になるという幸運な偶然です。したがって、ビットの反転はこの減算を効果的に実行します。

しかし、なぜ各桁の1との違いを見つけるのですか?まあ、あなたは違います。実際の目的は、指定された2進数と、桁数は同じで1だけの別の2進数との差を計算することです。たとえば、数値が10110001である場合、これらのビットをすべてフリップすると、事実上(11111111-10110001)を計算します。

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

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

11111111-10110001 + 1

あなたは何を得ますか?この:

100000000-10110001

これが最終的な方程式です。そして、これらの2つのステップを実行することで、最終的な違いを見つけようとしています。1桁余分にゼロを含む別の2進数から2進数を引いたものです。

しかし、なぜ私たちは本当にこの違いを後にしたのでしょうか?さて、これからは、 Wikipediaの記事 を読んだ方が良いと思います。

0

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

0
supercat

補数法による減算を実行する利点は、ハードウェアの削減です

0
user2640494

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

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

0
samoz