私はコンピュータシステムコースにいて、 Two's Complement と部分的に苦闘しています。私はそれを理解したいのですが、私が読んだすべてが私のために写真をまとめるわけではありません。私は ウィキペディアの記事 そして私の教科書を含む その他のさまざまな記事を読みました 。
それで、私はこれを community wiki postから始めて、Two's Complementが何であるか、使い方、そしてキャストのような操作の間にそれがどうやって数に影響するかを定義した演算とビットシフト演算.
私が望んでいるのは、 明確で簡潔な定義 であり、これはプログラマーによって容易に理解されます。
2の補数は整数を格納する賢い方法であるため、一般的な数学の問題は非常に実装が簡単です。
理解するために、あなたは2進法で数を考えなければなりません。
それは基本的に言う、
4バイトのミニバイトで試してみましょう(これを ニブル - 1/2バイトと呼びます)。
0000
- ゼロ0001
- one0010
- 20011
- 30100
から0111
- 4から7それは私達が肯定的に行ける限りです。 23-1 = 7.
ネガティブの場合:
1111
- 負の値1110
- 負の21101
- 負の31100
から1000
- 負の4から負の8負の値(1000
= -8)には、正の値ではありませんが、余分な値が1つあります。これは、0000
がゼロに使用されるためです。これは Number Line /と見なすことができます。
正数と負数を区別する
これを行うと、最初のビットが正と負の10進値を区別するために使用できるため、「符号」ビットの役割を果たします。最上位ビットが1
であれば、そのバイナリは負であると言えます。最上位ビット(最左端)が0
であるかのように、10進値が正であると識別できます。
「1の賛辞」の負の数は符号ビットを反転して0から数えるだけです。しかし、この方法では1000
を「負の0」として解釈しなければならず、混乱します。ハードウェアの近くで作業する場合は、一般的にこのことについて心配する必要があります。
私はそれがウィキペディアの記事よりももっとよく説明されることができるかどうか疑問に思います。
2の補数表現で解決しようとしている基本的な問題は、負の整数を格納する問題です。
まず、4ビットで格納されている符号なし整数を考えます。あなたは以下を持つことができます
0000 = 0
0001 = 1
0010 = 2
...
1111 = 15
これらが負であるか正であるかどうかについての兆候がないため、これらは符号なしです。
負の数を保存するには、いくつかのことを試すことができます。まず、最初のビットを+/-を表す符号ビットとして割り当て、残りのビットを振幅を表す符号絶対値表記を使用できます。 4ビットをもう一度使用し、1が - を意味し、0が+を意味すると仮定すると、
0000 = +0
0001 = +1
0010 = +2
...
1000 = -0
1001 = -1
1111 = -7
それで、あなたはそこに問題を見ますか?もっと大きな問題は、2進数の加減算です。符号の大きさを使って加減算する回路は非常に複雑になります。
何ですか
0010
1001 +
----
?
他のシステムは 超過表記 です。あなたは負の数を格納することができます、あなたは2つのゼロ問題を取り除きますが、足し算と引き算は困難なままです。
それで、2の補数が来ます。これで、正と負の整数を格納し、比較的簡単に算術演算を実行できます。数値を2の補数に変換する方法はいくつかあります。これが一つです。
数値を2進数に変換します(今のところは符号を無視します)。 5は0101、-5は0101
その数が正数であれば、完了です。例えば2は、2の補数表記を使用した2進数で0101です。
数が負の場合
3.1補数を見つける(0と1を反転させる)。 -5は0101なので、補数は1010です。
3.2補数に1を加える1010 + 1 =1011。したがって、2の補数の-5は1011です。
では、2 +(-3)をバイナリで実行したいとしたらどうでしょうか。 2 +(-3)は-1です。あなたがこれらの数を加えるために符号の大きさを使っていたならば、あなたは何をしなければならないでしょうか? 0010 + 1101 =?
2の補数を使用すると、それがどれほど簡単になるかを検討します。
2 = 0010
-3 = 1101 +
-------------
-1 = 1111
1111を10進数に変換する:
数字は1から始まるので、負の値になります。1111の補数は0000です。
0000に1を加えると、0001が得られます。
0001を10進数(1)に変換します。
= -1を適用します。
多田!
私が見たほとんどの説明のように、上のものは2の補数でどのように動作するかについては明らかですが、実際にそれらが であることを説明しないでください 私は、少なくとも整数についてそれをやろうとするでしょう、そして私はおそらく最初におなじみのいくつかの背景をカバーするつもりです。
10進数の場合の動作を思い出してください。
2345
は書き方です
2 ×103 + 3 ×102 + 4 ×101 + 5 ×10。
同じように、バイナリは同じ一般的な考え方に従って 0 および 1 を使用して数字を書く方法ですが、上記の10を2に置き換えます。それから、バイナリで、
1111
は書き方です
1 ×23 + 1 ×22 + 1 ×21 + 1 ×2
そして、あなたがそれをうまくいけば、それは15に等しいことがわかります(10進法)。それは
8 + 4 + 2 + 1 = 15.
これはすべて正で正数に適しています。人間が10進数を扱うときのように、マイナス記号をそれらの前に付けるだけの場合は、負の数に対しても有効です。それは一種のコンピュータでも可能ですが、私は1970年代初頭以来そのようなコンピュータを見たことがありません。理由は別の議論のために残します。
コンピュータの場合は、 補体 負数の表現です。そしてここで見落とされがちなことがあります。補数表記は、通常の正数の前に来る暗黙のゼロであっても、数の桁のある種の反転を含みます。質問が発生するので、それは厄介です:彼ら全員?それは考慮されるべき無限の桁数であり得る。
幸い、コンピュータは無限大を表すものではありません。数値は特定の長さ(または幅が好きな場合)に制限されます。それでは、正の2進数に戻りましょうが、特定のサイズを使います。これらの例では、8桁( "ビット")を使用します。だから私たちの2進数は本当になるでしょう
00001111
または
0 ×27 + 0 ×26 + 0 ×25 + 0 ×24 + 1 ×23 + 1×22 + 1 ×21 + 1 ×2
2の補数を負にするには、まずすべての(2進数)を補完して
11110000
そしてフォームに1を加える
11110001
しかし、それを理解するにはどうすればよいのでしょうか。
答えは、上位ビット(一番左のビット)の意味を変更したことです。このビットは、すべての負の数に対して 1 になります。変更はそれが現れる数の値に貢献の符号を変えることになるでしょう。だから今私たちの 11110001 は表すように理解されます
- 1 ×27 + 1 ×26 + 1 ×25 + 1 ×24 + 0 ×23 + 0×22 + 0 ×21 + 1 ×2
その表現の前に「 - 」が付いていることに注意してください。符号ビットが重み-2を持つことを意味します7つまり、-128(10進数)です。他のすべての位置は、符号なし2進数の場合と同じ重みを持ちます。
私たちの-15を解く、それは
-128 + 64 + 32 + 16 + 1
電卓で試してください。 -15です。
私がコンピュータで表される負の数を見た3つの主な方法のうち、2の補数は一般的な使用の便宜のために手を落とします。しかし、それは奇妙なことをしています。バイナリなので、偶数個の可能なビットの組み合わせがなければなりません。正の数は負の数とペアになることができますが、ゼロは1つだけです。ゼロを負にするとゼロになります。そのため、もう1つ組み合わせ、符号ビットに 1 、それ以外の場所に 0 を付けた数字があります。対応する正数は、使用されているビット数に収まりません。
この数についてさらに奇妙なことは、1を補完して加算することによってその正数を求めようとすると、同じ負数が返されることです。ゼロがこれを実行するのは当然のようですが、これは予想外であり、私たちが慣れている振る舞いではまったくありません。
これは奇妙な氷山の一角のようなものです。水面下にはもっとうそがついていますが、これで十分です。あなたが固定小数点演算のために "オーバーフロー"を研究するなら、あなたはおそらくもっと多くを見つけることができます。あなたが本当にそれに入りたければ、あなたはまた「モジュラ算術」を研究するかもしれません。
2の補数はバイナリの値を見つけるのに非常に役立ちますが、このような問題を解決するためのもっと簡潔な方法を考えました(他の誰もそれを公開したことはありません):
たとえば、バイナリを使用します。1101は、[スペース "1"が符号であると仮定して]-3と等しくなります。
2の補数を使用して、これを行います... 1101から0010への反転... 0001 + 0010 ===>の追加0011.正のバイナリ= 0011の0011です。したがって、1101 =-3!
私が実現したこと:
すべての反転と追加の代わりに、正のバイナリ(0101など)を解くための基本的な方法を行うことができます(23 * 0)+(22 * 1)+(21 * 0)+(2 * 1)= 5。
ネガティブでまったく同じ概念を実行してください!(小さなひねりを加えて)
1101を例にとります。
2ではなく最初の数3 * 1 =8、do-(23 * 1)=-8。
その後、通常通り続行し、-8+(22 * 1)+(21 * 0)+(2 * 1)=-3
あなたが有限の数のビット/トリット/ディジット/何でも持っていると想像してください。すべての数字が0であると0を定義し、自然に上向きにカウントします。
00
01
02
..
やがてあなたはあふれるでしょう。
98
99
00
2桁の数字があり、0から100までのすべての数字を表すことができます。これらの数字はすべて正数です。負の数も表したいとします。
私たちが本当に持っているのはサイクルです。 2より前の数は1です。1より前の数は0です。0より前の数は... 99です。
簡単にするために、50を超える数は負であるとしましょう。 「0」から「49」は0から49を表します。「99」は-1、「98」は-2、...「50」は-50です。
この表現は 10の補数 です。コンピュータは通常 2の補数 を使用します。これは、数字の代わりにビットを使用すること以外は同じです。
10の補数についてのいいところは、足し算ちょうどうまくいきますです。正数と負数を加算するために特別なことをする必要はありません。
2の補数は与えられた数の1から1の補数を加えることによって見つけられます。 10101
の2の補数を見つけ、次にその1の補数を見つける必要があるとしましょう。つまり、01010
はこの結果に1
を追加する、つまり01010+1=01011
は最終的な答えです。
8ビットを使ってバイナリ形式で10 - 12の答えを得ることができます。実際にやることは10 +(-12)です
10から引くためには12の補数部分を取得する必要があります。バイナリの12は00001100です。バイナリの10は00001010です。
12の補数部分を取得するには、すべてのビットを反転してから1を加算します。バイナリ反転の12は11110011です。これは逆コード(1の補数)でもあります。今それを追加する必要があります、それは今11110100です。
だから11110100は12の賛辞です!このように考えると簡単です。
これで、10 - 12の上記の問題をバイナリ形式で解決できます。
00001010
11110100
-----------------
11111110
オドメーターを例にして、jngによる素晴らしい説明 on Reddit を読みました。
これは便利な規約です。 2進数で正の数を加算/減算する同じ回路と論理演算は、規則を使用する場合でも正と負の両方の数で機能します。これが、とても便利で遍在的な理由です。
車の走行距離を想像してみてください、それは99999で転がります(例えば)あなたが00000を増加させるなら00001を得ます。あなたが00000を減少させるならあなたは99999を得ます(ロールアラウンドのため)。 1を99999に戻すと、00000に戻ります。したがって、99999は-1を表すと判断すると便利です。同様に、99998が-2を表すと判断すると非常に便利です。あなたはどこかでやめなければなりません、そして同じく慣例により、数字の上半分は負であると考えられ(50000-99999)、そして下半分の正はただ彼ら自身(00000-49999)を表します。結果として、上の桁が5から9であることは表現された数が負であることを意味し、それは0から4であることは表現されることが正であることを意味します。
これを理解することは私にとっても困難でした。私がそれを入手して本の記事や説明を再読するために戻った(当時はインターネットがありませんでした)。私はその後アセンブリ言語を教える本を書きました(それは10年間でかなり売れました)。
数学の観点から2の補数システムを見ることは本当に意味があります。 10の補数では、アイデアは本質的に違いを「分離」することです。
例:63 - 24 = x
24の補数を追加します。これは本当に(100 - 24)です。だから、私たちがしているのは、方程式の両側に100を加えることだけです。
これで、方程式は100 + 63 - 24 = x + 100になります。これが、100(または10または1000など)を削除する理由です。
長い一連のゼロから1つの数を引かなければならないという不都合な状況のために、10進法では9の補数で、「基数補数の減少」システムを使用します。
大きな9個のチェーンから減算された数字が表示されたら、その数字を元に戻すだけです。
例:99999 - 03275 = 96724
それが、9の補数の後に1を加える理由です。子供の頃の数学からおそらくご存知のように、9は「盗む」ことによって10になります。
バイナリでは、2の補数は10の補数に等しく、1の補数は9の補数になります。主な違いは、10のべき乗で差を分離しようとする代わりに(10、100などを方程式に加える)、2のべき乗で差を分離しようとしているということです。
このため、ビットを反転します。私たちの被減数が10進数で9のチェーンであるのと同じように、私たちの被減数はバイナリで1のチェーンです。
例:111111 - 101001 = 010110
1の連鎖は2のニース乗より1小さいため、9の10進数のように1から差をつけて1を盗みます。
負の2進数を使っているとき、私たちは本当にただ言っているだけです:
0000 - 0101 = x
1111 - 0101 = 1010
1111 + 0000 - 0101 = x + 1111
Xを「分離」するには、1111が10000から1離れているため1を追加する必要があり、元の差に追加したため先頭の1を削除します。
1111 + 1 + 0000 - 0101 = x + 1111 + 1
10000 + 0000 - 0101 = x + 10000
Xを得るために両側からただ10000を取り除いてください、それは基本的な代数です。
答えの多くは、これまで2の補数が負の数を表すのに使われる理由をうまく説明していますが、2の補数が何であるか、特に「1」が加えられる理由を教えてくれません。
この混乱は、補数の定義の理解が不十分なことから来ています。補数は何かを完全にするだろう欠けている部分です。
基数bのn桁の数xの基数の補数は、定義上、b ^ n-xです。 2進数では4は100で表され、3桁(n = 3)と2の基数(b = 2)を持ちます。したがって、その基数の補数はb ^ n-x = 2 ^ 3-4 = 8-4 = 4(または2進数で100)です。
しかし、2進法で基数の補数を取得することは、基数の補数より1だけ少ない、(b ^ n-1)-yとして定義される基数の補数の減少を取得することほど容易ではありません。基数の補数を減らすには、すべての数字を反転するだけです。
100 - > 011(基数補数の減少)
基数(2の)補数を得るために、定義として定義されているように単に1を加えます。
011 + 1 - > 100(2の補数).
この新しい理解を得て、Vincent Ramdhanieの例を見てみましょう(上記の2番目の回答を参照)。
/ * Vincentのスタート
1111を10進数に変換する:
数値は1から始まるので負であるため、1111の補数は0000です。1を0000に加算して、0001を取得します。0001を10進数に変換します。つまり、1になります。多田!
vincentの終わり* /
として理解されるべきです
数字は1から始まるので、負です。だから我々はそれがある値xの2の補数であることを知っている。その2の補数で表されるxを見つけるには、まずその1の補数を見つける必要があります。
xの2の補数:1111 xの1の補数:1111-1 - > 1110。 x = 0001、(全桁反転)
記号 - を適用し、答え= -x = -1とします。
2の補数:数字の1の補数を追加すると、2の補数が得られます。例:100101 1の補数は011010であり、2の補数は011010 + 1 = 011011(1の補数を含むものを追加することによって) 詳細については、 この記事ではグラフで説明しています。
データ型のビットの組み合わせの約半分が負の整数用に予約され、負の整数の大部分を対応する正の整数と加算するとキャリーオーバーフローが発生するように負の整数をエンコードする賢い方法です。結果は2進ゼロになります。
したがって、2の補数で1が0x0001の場合、-1は0x1111になります。これは、合計が0x0000になる(オーバーフローが1になる)ためです。
私は数週間前に同じ問題を抱えていました。結局私はそれを正しく理解していることを確認するために、さまざまな情報源からオンラインでそれを読み、それらをまとめることを試み、そして自分自身でそれについて書いています。主に2つの理由で、2の補数を使用します。
あなたが当面の問題のより詳細な説明が欲しいならば、私によって書かれた記事 ここ を試してください。それが役に立てば幸い!
私はlavinioの答えを気に入っていましたが、ビットをシフトするといくらか複雑になります。多くの場合、符号ビットを尊重しながら、または符号ビットを尊重しないでビットを移動させることができます。これは、数字を符号付き(ニブルの場合は-8から7、バイトの場合は-128から127)またはフルレンジの符号なしの数字(ニブルの場合は0から15、バイトの場合は0から255)として扱うかどうかの選択です。
Wordの補完は完全性に由来します。 10進数の世界では、0から9までの数字は補数(完全なセット)の数字または数字記号を提供して、すべての10進数を表します。 2進数の世界では、数字0と1は補数の数字を提供して、すべての2進数を表します。実際、記号0と1は、すべて(テキスト、画像など)、および正(0)と負(1)を表すために使用する必要があります。私たちの世界では、数値の左側の空白はゼロと見なされます。
35=035=000000035.
コンピューターの保管場所には、空白スペースはありません。すべてのビット(2進数)は0または1である必要があります。メモリを効率的に使用するために、8ビット、16ビット、32ビット、64ビット、128ビットの表現としてメモリ番号を保存できます。 8ビットの数値として保存されている数値を16ビットの場所に転送する場合、符号と絶対値(絶対値)は同じままにする必要があります。 1の補数表現と2の補数表現の両方がこれを促進します。名詞として:1の補数と2の補数は両方とも、最上位ビット(左側のビット)が符号ビットである符号付き量のバイナリ表現です。 0は正、1は負です。 2の補数は負の意味ではありません。これは、署名された数量を意味します。 10進数の場合、大きさは正の量として表されます。この構造は、より多くのビットでレジスタ[]にプロモートするときに符号拡張を使用して数量を保持します。
[0101]=[00101]=[00000000000101]=5 (base 10)
[1011]=[11011]=[11111111111011]=-5(base 10)
動詞として:2の補数は否定するを意味します。ネガティブにするという意味ではありません。負の場合は正になります。正の場合は負にします。大きさは絶対値です:
if a >= 0 then |a| = a
if a < 0 then |a| = -a = 2scomplement of a
この機能により、否定と追加を使用した効率的なバイナリ減算が可能になります。 a-b = a +(-b)
1の補数を取る公式の方法は、各桁が1からその値を減算することです。
1'scomp(0101) = 1010.
これは、各ビットを個別に反転または反転することと同じです。これは負のゼロをもたらしますが、これはあまり愛されていないため、1の補数に1を追加すると問題がなくなります。 2の補数を否定または取得するには、最初に1の補数を取得してから1を追加します。
Example 1 Example 2
0101 --original number 1101
1's comp 1010 0010
add 1 0001 0001
2's comp 1011 --negated number 0011
例では、符号拡張された数値でも否定が機能します。
追加:
1110キャリー111110キャリー0110は000110 1111 111111合計0101合計000101と同じです
引き算:
1110 Carry 00000 Carry
0110 is the same as 00110
-0111 +11001
---------- ----------
sum 0101 sum 11111
2の補数を使用する場合、数値の左側の空白スペースは正の数値の場合はゼロで埋められますが、負の数値の場合は1で埋められます。キャリーは常に追加され、1または0でなければなりません。
乾杯
数値をビットごとに補完するとは、その中のすべてのビットを反転することです。それを補完するために、すべてのビットを反転して1を加えます。
符号付き整数に2の補数表現を使用して、2の補数演算を適用して、正の数を負の数に変換したり、その逆の変換を行います。そのため、例としてニブルを使用すると、0001
(1)は1111
(-1)になり、再びopを適用すると、0001
に戻ります。
ゼロでの演算の振る舞いは、正および負のゼロを特別に処理しなくても、ゼロを1つの表現で表すことができます。 0000
は1111
を補完するものです。正と負の値ではなく、1つの0を与えて、0000
にオーバーフローします。
この表現の主な利点は、符号なし整数の標準的な加算回路をそれらに適用したときに正しい結果が得られることです。たとえば、1と-1をニブルに追加すると0001 + 1111
、ビットがレジスタからオーバーフローし、0000
が残ります。
優しい紹介のために、すばらしいComputerphileは 主題に関するビデオを制作しました 。
参照: https://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html
すべてのビットを反転して1を加えます。
// in C++11
int _powers[] = {
1,
2,
4,
8,
16,
32,
64,
128
};
int value=3;
int n_bits=4;
int twos_complement = (value ^ ( _powers[n_bits]-1)) + 1;
2の補数は、本質的に2進数の加法逆数を計算する方法です。自問してみてください:バイナリ形式の数値が与えられた場合、元の数値に追加するとどのビットパターンが結果をゼロにしますか?このビットパターンを思い付くことができる場合、そのビットパターンは元の数の-ve表現(加算逆数)です。定義上、その加算逆数に数値を追加すると、常にゼロになるはずです。例:10進数の5である101を取得します。今度は、特定のビットパターン(101)に追加するとゼロになるビットパターンを作成します。これを行うには、101の右端のビットから開始し、個々のビットごとに、同じ質問を繰り返します。結果をゼロにするために、「this」ビットにどのビットを追加する必要がありますか?通常のキャリーオーバーを考慮して、それを続けます。右端3桁(先頭のゼロに関係なく元の数値を定義する数字)で処理が完了すると、最後のキャリーは加法逆のビットパターンになります。さらに、元の数値を1バイトで保持できるため、加算逆数の他のすべての先行ビットも1である必要があります。これにより、コンピュータが「that」ストレージタイプ(char)を使用して数値とその加算逆数を加算しますその文字の結果はすべてゼロになります。
1 1 1
----------
1 0 1
1 0 1 1 ---> additive inverse
---------
0 0 0
与えられた数の2の補数はnoです。の1の補数に1を加えることによって得られます。バイナリ番号があると仮定します。:10111001101それは1の補数です:01000110010そしてそれは2の補数はなります:01000110011