-1を2進数で表すために、2の補数が使用される理由があるかどうか知りたいだけです。ビットを反転して1を追加しますか?
-1は、10000001ではなく11111111(2の補数)で表されます。これは、最初のビットが負のフラグであるバイナリ1である10000001です。
免責事項:私は自分の仕事のためにバイナリ算術に依存していません!
-1を2進数で表すために、2の補数が使用される理由があるかどうか知りたいだけです。ビットを反転して1を追加しますか?
-1は、10000001ではなく11111111(2の補数)で表されます。これは、最初のビットが負のフラグであるバイナリ1である10000001です。
免責事項:私は自分の仕事のためにバイナリ算術に依存していません!
回答:
これは、加算が負の数を処理するための特別なロジックを必要としないように行われます。ウィキペディアの記事をチェックしてください。
たとえば、2と-1の2つの数値があるとします。数値を表す「直感的な」方法では、数値はそれぞれ0010
と1001
になります(サイズは4ビットに固執しています)。では2の補数の方法、彼らはある0010
と1111
。今、それらを追加したいとしましょう。
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つの小さな追加手順です。
how we arrived at 2s compliment the first place.
cs.cornell.edu/~tomf/notes/cps104/twoscomp.html
1
示し-8
、そして残りの3つの1
Sが示し4
、2
および1
ので、それぞれ-8+4+2+1 = -1
。
ウィキペディアはそれをすべて言います:
2の補数システムには、加算および減算回路がオペランドの符号を調べて加算するか減算するかを決定する必要がないという利点があります。この特性により、システムの実装が簡単になり、高精度の演算を簡単に処理できるようになります。また、ゼロは単一の表現のみを持ち、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の12の補数が0100であることです。これは、2の補数システムで-12を表すことを除いて、+8も曖昧に表します。
この問題は、正の数の左端のビットが常に0である場合に解決されます。その場合、2の補数は常に左端のビットに1があり、2の補数と+ ve数を表す同じビットのセットのあいまいさはありません。
この操作の通常の実装は「ビットを反転して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へのキャリーは「オーバーフロー」であり、機能するとは想定されていないため、ほとんどすべての算術演算は変更なしで機能します(他の人が指摘したとおり)。符号の大きさは、通常、符号ビットを検査し、異なるロジックを使用します。
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内の同じ回路で実行できます。
他の回答を拡張するには:
2の補数で
除算には別のメカニズムが必要です。
2の補数は通常のモジュラー算術であるため、これらすべてが真です。
unsigned mul(unsigned short x, unsigned short y) { return x*y; }
[16ビット短いです。32ビットのint]は時折製品が2147483647より大きい場合に誤動作するコードを生成する
この質問の答えを読んで、私はこのコメントに出くわしました[編集]。
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バイト長ですint
とfloat
は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
:IEEE 754単精度表記の4バイト[編集]この投稿は、user4581301によるコメントの後に編集されました。いくつかの役立つ行を削除する時間を割いていただきありがとうございます!
int x = -4
、そしてprintf("%d" , x)
どのように解釈されるのですか?また、unsigned int
and signed int
と%d
andの違いは何%u
ですか...これは長い間私を悩ませてきました。ありがとう。
int
タイプを使用する場合、signed
修飾子はデフォルトです。つまり、int
とsigned int
はまったく同じタイプです。したがって、2つの定義int i = -4;
とsigned int i = -4;
同じ意味を持っています。
int
4つのバイトで2の補数とunsigned int
の4バイトであるプレーンバイナリ表記(使用して、システム上の実際の型のサイズを確認sizeof
演算子)。
スタンフォード大学のYouTubeチャンネルで視聴できるプログラミングパラダイムと呼ばれる一連の講義の2番目の講義(2の補数に関する説明は13:00頃に始まります)で、スタンフォード大学のジェリーケイン教授が2の補数を説明するのを見ることができます。レクチャーシリーズへのリンクは次のとおりです:http : //www.youtube.com/view_play_list?p=9D558D49CA734A02。
まあ、あなたの意図は本当にあなたの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桁余分に減算し、最も有意なビット位置を除いてゼロを含むことです。
しかし、なぜこの違いの後に私たちは本当にハンカーしているのですか?さて、これからは、ウィキペディアの記事を読んだ方がいいと思います。
補数法で減算を実行する利点は、ハードウェアの
複雑さが軽減されることです。加算と減算に別のデジタル回路を使用する必要がありません。加算と減算の両方が加算器のみで実行されます。
ここでまだ言及されていない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つあることです。
補数表現の問題は、0の値が2つあることです。
しかし、2の補数表現を使用する場合、0の値は1つだけであるため、2の補数形式で負の数を表します。
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にアクセスしてください