-1を2進数で表すために、2の補数が使用される理由があるかどうか、私はただ興味があります。ビットを反転して1を加算しますか?
-1は、最初のビットが負のフラグであるバイナリ1である10000001ではなく(私にとってはより直感的に)11111111(2の補数)で表されます。
免責事項:私は自分の仕事を2進演算に依存していません!
-1を2進数で表すために、2の補数が使用される理由があるかどうか、私はただ興味があります。ビットを反転して1を加算しますか?
-1は、最初のビットが負のフラグであるバイナリ1である10000001ではなく(私にとってはより直感的に)11111111(2の補数)で表されます。
免責事項:私は自分の仕事を2進演算に依存していません!
これは、加算に負の数を処理するための特別なロジックが必要ないようにするためです。ウィキペディアの記事をチェックしてください。
2と-1の2つの数字があるとします。数値を表す「直感的な」方法では、それぞれ0010
とになり1001
ます(サイズは4ビットに固執しています)。2の補数の方法では、それらは0010
と1111
です。さて、それらを追加したいとしましょう。
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を格納することには、もう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つにおける小さな追加のステップです。
ウィキペディアはそれをすべて言います:
2の補数システムには、加算および減算回路がオペランドの符号を調べて、加算するか減算するかを決定する必要がないという利点があります。このプロパティにより、システムの実装が簡単になり、より高精度の演算を簡単に処理できるようになります。また、ゼロには1つの表現しかないため、1の補数システムに存在する負のゼロに関連する微妙な点がなくなります。
言い換えれば、数が負であるかどうかにかかわらず、加算は同じです。
この質問は古いですが、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の補数が0100であるということです。これは、2の補数システムで-12を表す以外に、+8もあいまいに表します。
この問題は、正の数の左端のビットが常に0である場合に解決されます。その場合、2の補数は常に左端のビットに1があり、2の補数と+ve数を表す同じビットセットのあいまいさはありません。
2の補数を使用すると、加算と減算を通常の方法で実行できます(符号なしの数値を巻くように)。また、-0(数値を比較する通常のビットごとの方法では0と等しくない0を表す別の方法)も防止します。
これは、数値の合計と差を単純化するためです。2の補数で成文化された負の数と正の数の合計は、通常の方法でそれらを合計するのと同じです。
2の補数を使用すると、特別なロジックなしで負の数と正の数を加算できます。
メソッド10000001(-1)
+00000001(1)を使用して1と-1を追加しようとすると、
10000010(-2)
が得られます。
代わりに、2の補数を使用して、次のように追加できます。
11111111(-1)
+00000001(1)
00000000(0)を取得します
減算についても同じことが言えます。
また、6(2つの正の数)から4を減算しようとすると、2の補数4を加算し、2を合計することができます6 +(-4)= 6-4 = 2
これは、正の数と負の数の両方の減算と加算がすべてCPU内の同じ回路で実行できることを意味します。
操作の通常の実装は「ビットを反転して1を加算する」ですが、それを定義する別の方法があり、おそらく理論的根拠がより明確になります。2の補数は、各ビットが2の次の累乗を制御する通常の符号なし表現を取り、最も重要な項を負にする場合に得られる形式です。
8ビット値を取るa7a 6 a 5 a 4 a 3 a 2 a 1 a 0
通常の符号なしバイナリ解釈は次のとおりです。27 *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の補数の解釈は次のとおりです。
-27 * 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に持ち込むことは「オーバーフロー」であり、機能することは期待されていないため、ほとんどすべての算術演算は変更なしで機能します(他の人が指摘しているように)。符号の大きさは通常、符号ビットを検査し、異なるロジックを使用します。
他の人の答えを拡張するには:
2の補数で
分割には別のメカニズムが必要です。
2の補数は通常のモジュラー算術であり、モジュロを減算することによっていくつかの数値を負と見なすことを選択するため、これらはすべて当てはまります。
この質問への答えを読んで、私はこのコメント[編集済み]に出くわしました。
2の0100(4)の補数は1100になります。通常、1100は12になります。つまり、通常の1100と言うと12になりますが、2の補数1100と言うと-4になりますか?また、Javaでは1100(今のところ4ビットと仮定します)が格納されている場合、それが+12か-4かをどのように判断しますか?–hagrawal7月2日16:53
私の意見では、このコメントで尋ねられた質問は非常に興味深いので、まずそれを言い換えてから、答えと例を提供したいと思います。
質問–システムは、1つ以上の隣接するバイトをどのように解釈する必要があるかをどのように確立できますか?特に、システムは、特定のバイトシーケンスがプレーンな2進数であるか、2の補数であるかをどのように確認できますか?
答え–システムは、タイプを介してバイトのシーケンスを解釈する方法を確立します。タイプは定義します
例–以下では、
char
の長さは1バイトですshort
の長さは2バイトですint
'sとfloat
'sは4バイト長ですこれらのサイズは私のシステムに固有であることに注意してください。かなり一般的ですが、システムごとに異なる場合があります。システム上でそれらが何であるかを知りたい場合は、sizeof演算子を使用してください。
まず、4バイトを含む配列を定義し、それらすべてを10111101
16進数に対応する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 int
、int
および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
:IEEE754単精度表記で4バイト[編集]この投稿は、user4581301によるコメントの後に編集されました。これらのいくつかの役立つ行を削除するために時間を割いていただきありがとうございます!
一部の初期の加算機では、デジタルコンピュータの時代以前は、各キーに異なる色の凡例のセットを使用して値を入力することによって減算が実行されることに注意してください(したがって、各キーは9から数値を引いたものを入力します)減算)、および特別なボタンを押すと、計算へのキャリーが想定されます。したがって、6桁のマシンでは、値から1234を引くために、オペレーターは通常「998,765」を示すキーを押し、ボタンを押してその値に1を加えた値を進行中の計算に追加します。2の補数演算は、以前の「10の補数」演算と単純に2進数で同等です。
補数法による減算を実行する利点は、ハードウェアの
複雑さが軽減されることです。加算と減算のために異なるデジタル回路を必要としません。加算と減算の両方が加算器によってのみ実行されます。
2の補数は、回路への実装が簡単で、負のゼロを許可しないために使用されます。
xビットがある場合、2の補数は+(2 ^ x / 2 + 1)から-(2 ^ x / 2)の範囲になります。1の補数は+(2 ^ x / 2)から-(2 ^ x / 2)まで実行されますが、負のゼロを許可します(4ビット1の補数システムでは0000は1000に等しい)。
ええと、あなたの意図は実際にはあなたの2進数のすべてのビットを逆にすることではありません。実際には、1から各桁を減算します。1から1を減算すると0になり、1から0を減算すると1になります。したがって、ビットを反転すると、この減算が効果的に実行されます。
しかし、なぜ各桁の1との違いを見つけているのですか?まあ、あなたは違います。実際の目的は、指定された2進数と、同じ桁数で1のみを含む別の2進数との差を計算することです。たとえば、番号が10110001の場合、これらすべてのビットを反転すると、効果的に計算されます(11111111-10110001)。
これは、2の補数の計算の最初のステップを説明しています。次に、2番目のステップである1を追加することも写真に含めましょう。
上記の2進方程式に1を追加します。
11111111-10110001 + 1
何がもらえますか?これ:
100000000-10110001
これが最終的な方程式です。そして、これらの2つのステップを実行することで、これを見つけようとしています。最終的な違いは、2進数を、最上位のビット位置を除いてゼロを含む1桁の余分な数字で別の2進数から減算したことです。
しかし、なぜ私たちはこの違いの後に本当にハンケリンなのですか?さて、これからはウィキペディアの記事を読んだ方がいいと思います。
加算と減算の両方に対して加算演算のみを実行します。加算のために、第1オペランドに第2オペランドを追加します。減算の場合、第2オペランドの2の補数を第1オペランドに加算します。
2の補数表現では、減算のために個別のデジタルコンポーネントは必要ありません。加算器と補数のみが使用されます。
ここでまだ言及されていない2の補数表現の主な利点は、2の補数の合計、差、または積の下位ビットが、オペランドの対応するビットにのみ依存することです。-1の8ビット符号付き値の理由は、下位8ビットがである他の整数から下位8ビットがである整数を11111111
減算すると、下位8ビットが00000001
0000000
11111111
。数学的には、値-1は1の無限の文字列になりますが、特定の整数型の範囲内のすべての値は、特定のポイントを過ぎたすべて1またはすべて0になるため、コンピューターが「符号拡張」するのに便利です。 1または0の無限の数を表すかのように、数値の最上位ビット。
2の補数は、バイナリマシンの自然なワードサイズよりも大きい型を処理するときに適切に機能する唯一の符号付き数値表現です。加算または減算を実行するときに、コードは各オペランドの最小チャンクをフェッチし、の最小チャンクを計算できるためです。結果を格納し、次に各オペランドの次のチャンクをロードし、結果の次のチャンクを計算して、それを格納するなど。したがって、単一の8ビットレジスタを通過するためにすべての加算と減算を必要とするプロセッサでも32ビットの符号付き数値をかなり効率的に処理できます(もちろん、32ビットのレジスタよりも低速ですが、それでも機能します)。
C標準で許可されている他の符号付き表現を使用する場合、結果のすべてのビットがオペランドの任意のビットの影響を受ける可能性があるため、値全体を一度にレジスタに保持するか、追加の計算を行う必要があります。少なくとも場合によっては、結果の各チャンクの読み取り、変更、および再書き込みが必要になるステップ。
表現にはさまざまな種類があります。
-正の数のみを表すために使用される符号なし数値表現
-正の数と負の数を表すために使用される符号付き数値表現。符号付き数値表現では、MSBビットは符号ビットを表し、残りのビットは数値を表します。MSBが0の場合は数値が正であることを意味し、MSBが1の場合は数値が負であることを意味します。
符号付き数値表現の問題は、0に2つの値があることです。
1の補数表現の問題は、0に2つの値があることです。
ただし、2の補数表現を使用する場合、0の値は1つだけになるため、2の補数形式で負の数を表します。
いくつかの状況で重要なわずかな補遺があります。これらの制約が与えられた場合に可能な唯一の表現は、2つの褒め言葉です。
理由を理解するために、カーディナリティを減らすのに役立ちます。たとえば、Z_4。
符号と大きさ、および人の褒め言葉は両方とも、同じ数の要素でリングを形成しません。症状はダブルゼロです。したがって、エッジでの作業は困難です。数学的に一貫性を保つには、オーバーフローまたはトラップ表現をチェックする必要があります。
Two2の補数がOneの補数システムではなく負の数を表すために使用される理由の1つの満足のいく答えは、Twoの補数システムが0の複数の表現の問題と、負の表現のOneの補数システムに存在するエンドアラウンドキャリーの必要性を解決することです。数字。
詳細については、https://en.wikipedia.org/wiki/Signed_number_representationsをご覧ください。
End-around-carryについては、 https://en.wikipedia.org/wiki/End-around_carryにアクセスしてください。