メモ帳などの等幅フォント・固定幅で読んでください。タブが半角8桁でないと図が崩れます。半角で80桁、全角40桁以上無いと読み辛いと思います。 『コンピュータとマイナスの数』by baa 初出2013/12、2014/1修正、2014/2修正、2014/10加筆  長文なので目次 ・コンピュータは、命令書を読む電卓 ・機械の言葉は二進数 ・マイナスの数を機械で表現する ・コンピュータは、その数の正負を知らない ・正負の見なし方。それは貴方が決める ・ちょっと脱線。コンピューターは人工無脳であるか? ・まとめと、コンピュータによる悟り  「コンピュータとか二進数とか関係ネー」、暇つぶしの読み物として読まれる方は(居るのか?)、“ちょっと脱線。コンピューターは人工無脳であるか?”から後の節が、この長文のまとめと、一応読み物らしき物になっていますので、後は全部飛ばして下さい。  2番目の“機械の言葉は二進数”までは、コンピュータが内部では二つの値 = 2値で全てのデータを扱う事の超簡単な“ややこしい説明”なので(えっ?)、先刻ご承知の方は3番目の“マイナスの数を機械で表現する”から読んで下さい。 −−−−−−で挟んだ部分は、寄り道なので、一通り全体を読んで話を掴んでから読んだ方が、分かりやすいかもしれません。 ここから本文 ● コンピュータは、命令書を読む電卓  コンピュータは、文字でも画像でも音声でも、内部では数に当てはめます。コンピュータは、電子回路内の特定の“状態”を数に見立てて、数を取り扱う“計算機”です。普通の「電卓(電子卓上計算機)」も一種のコンピュータですが、「パソコン」と呼んでいる様なコンピュータとの違いは、パソコンの方は、そのパソコンが理解できる書式に従った、任意の(時にはとても長い)命令書(=プログラム)を読み込んで記憶し、それを解釈して、そのアルゴリズム(論理手順)通りに計算を実行する事が出来るという点です。  つまりコンピュータは“命令書を読んでその通り計算する汎用の計算機械”です。  機械に計算をさせるためには、まず機械に数というものを扱わせなければなりません。機械に数を扱わせるには、例えば、そろばんの珠の並び方でも、歯車の回転位置でも、水の水位でも、何でもいいですから、機械自身で違いを判定できる特定の“状態”を「この状態は1」、「この状態は2」という風に数に見立てれば良いのです。ですが、そろばんの珠や歯車を数に見立てて動かしていたのでは、多少とも計算に時間が掛かりますし、人のオツムより計算が遅くては、せっかく機械に計算させる意味の半分は無くなってしまいます(もう半分は正確さです)。そろばんの珠でも、歯車でも、物理的に物を動かすには力が必要ですし、機械もそれだけの大きさが必要です。  コンピューターでは、人より高速で数を扱わせる為に、人間が扱える物の内、最も速い部類である「電気」の“状態”を使う事に決めたのです。回路に流れる「電気」の量を“状態”とみなせば、物理的に他の物を動かす必要が(殆ど)有りませんし、機械も小型化できます。 ● 機械の言葉は二進数  どんなものでも機械を作る場合は、できるだけ簡単な仕組みにする方が動作が速くなります。計算機を作る場合は、最小で2つの違った“状態”があれば、それを組み合わせて全ての数(どころか、文字でも音でも画でも、数値で符号化できる物は全て)を“符号”として表現できるので、機械の中に最低限2つの“状態”を作って、それを“符号”を表すのに必要な量だけ同時に一定時間保存でき、それが機械自身で見分けられれば良いわけです(もちろん、それを使って計算出来なければなりませんが)。  例えば、2つの“状態”を幾つか組み合わせて意味を持たせ、機械がこういう設定の場合に、この“状態の並び”は数の65を意味し、文字のAを表示する。同じ設定で、また別の“状態の並び”は数の255を意味し、カーソルを表示する、という様な具合です。 2つの状態を組み合わせて並べ、意味を持たせる(つまり符号化する) 例)白と黒の2種類の箱を8個並べて、何か表す   □■□□■■□■ これを77と決める。77ならMを書くとする   □■□■□□■■ これを83と決める。83ならSを書くとする   □■□■■□□□ これを88と決める。88ならXを書くとする   では、こんな並び方の“符号”なら、何と書くでしょうか?(^^;)   □■□□■■□■ □■□■□□■■ □■□■■□□□  前節で書いたように、コンピュータを考えられた先人達は、回路に流れる電気を使って2つの“状態”を表現することにしました。電気の“状態”を見分けるには、例えば電燈スイッチのオン/オフのように、回路の電流有り/電流無し、が一番分かり易いですが、実際にその方法で計算機を作ってみると、電流がほぼ0の状態から、流れているとハッキリ判別できる量の電流まで上げ下げして安定させるのは、思いのほか時間が掛かりました。  その点を考慮して、もっと高速に“状態”を安定させられる「電圧が一定より低い」と「電圧が一定より高い」を、二進数の一つの桁と見立てることにしました。 例)電圧5Vなら流れている、0Vなら流れていないとする        −−−−−−−−−− ┐     5V ────────── │この範囲なら流れている  一定の高さ↑−−−−−−−−−− ┘                     ←どちらでもない  一定の高さ↓−−−−−−−−−− ┐     0V ────────── ┘この範囲なら流れていない  現在のコンピュータと周辺機器の内部では、全てのデータが「電圧が一定より低い/高い」や「磁極のN/S」「レーザー光線を反射する/しない」の様な何らかの2つの“状態”で保存されています。その2つの“状態”を2つの“値”とみなし、それを二進数の一つの桁と見立てるのです。  「では、二進数とは何ですか?」というと、まず日ごろ私達人間の使う十進法の数=十進数を考えてみると、十進数を表記するときには、0〜9の九つの文字を使って、0、1、2と数を数えていき、十になると桁が1つ上がってその桁は0となり、左隣に1を書いて10、11、12・・・と文字を書いていきますよね。10倍になると桁が一つ上がるわけです。十進数は“0から9の10個の記号を使って表記し、10倍になると桁が一つ上がる”という数の表現方法です。 十進数  10個の記号を使って表記   0、1、2、3、4、5、6、7、8、9  10倍になると桁が上がる      1─┐       10倍     10←┤       10倍    100←┤       10倍   1000←┤        :  二進法の数=二進数を人間が使う場合は、0と1の二つの文字だけを使って表現し、ある桁の値が十進数での2相当になると桁が一つ上がって、初めの桁は0になり、左隣に1を書きます。2倍になると桁が一つ上がるので二進数です。“0と1の2個の記号を使って表記し、2倍になると桁が一つ上がる”という数の表現方法です。 二進数  2個の記号を使って表記   0、1  2倍になると桁が上がる      1─┐        2倍     10←┤        2倍    100←┤        2倍   1000←┤        : 十進数と二進数の対応  十進数 二進数   0 0   1 1   2 10   3 11   4 100   5 101   6 110   7 111   8 1000 ←4桁   9 1001  10 1010   : :  十進数の8の時点で、二進数では既に4桁です。どんどん桁が増えていくのに 0 と 1 ばかりで読み取りにくく、二進数は人間向きではないようです。やはり人間様には十進数、機械の言葉は仕組みが単純な二進数、というのが一番扱いやすいようです。コンピュータでは、二進数の一つの桁を1ビット(binary digitの略でbit)と呼びます。8ビットを1組にまとめて1バイト(byte)と呼びます(注1)。  現在のコンピュータでは多くの場合、2つの違った状態を表現して保存できる最小の回路の電圧、例えばメモリの1ビットの電圧が、一定より低い状態を0、一定より高い状態を1、として二進数に当てはめるように設計されているそうです。 注1.1990年代には、4ビットをニブル(nibble)、16ビットをワード(word)、    32ビットをダブルワード(double word)と呼んだように記憶していますが、    現代では、ビットとバイト以外はプログラミング言語や環境によって、呼び方    や区切り方が違っていると思います(?)。 ● マイナスの数を機械で表現する  ここからやっと本題です(^^;)。数には負(マイナス)の数もあって、実生活で計算する時に負の数がないと、とても困ります。コンピュータが計算する時にも、負の数を扱わせるには、どうすれば良いでしょうか?  人が負の数を扱うときには、これは負の数ですよという「印」として、数字に「負の符号」の−を付けます。コンピュータにも「負の符号」に相当する何らかの「印」を扱えるように設計すればいいのでしょうが、あまり機械が複雑になっても作るのが大変ですし、その為に動作が遅くなっても困ります。まず、考えられたのが一番上の桁をマイナスの符号に見立てる事でした。例えば、二進数の4桁 = 4ビットで考えると、 十進 二進  : :  4 0100  3 0011  2 0010  1 0001  0 0000 −1 1001 ← 負の数は、一番上の桁が1であるとする −2 1010 ← その点以外は、正の数と同じ表現 −3 1011 ← −4 1100 ←  : :  という具合です。4ビットの1は 0001 で、その一番上の桁を 1 として 1001 とすれば−1となるわけです。ある本によると、実際にこの負の数の表現方法を使ったコンピュータが在ったそうです。  ところがこの方法では、正の数用、負の数用の2通りの計算回路と、正の数と負の数を足し引きする計算回路の、計3回路を別に作り、上手く組み合わせて動作させなければならないので大変です。前節で書いた通り、回路は単純なほど、動作が速くて作りやすいのです。  他にも、これとは別の負の数の表現方法を考え、実際にその方法で作られたコンピュータが在ったそうですが、どれも回路が複雑になってしまうのでした。  そこで、数学っぽく「負の数Aとは、正の数Aに足して0になる数」とすれば良いのではと考えた(のか、二進数のビット列をいじって色々試している内に発見したのか? その)人が居ました(名前失念、スイマセン)。例えば、1に−1を足せば0ですから、仮に二進数4ビットで考えると、 二進数4ビットの1→ 0001 足して0になる数→  1111 ←これを−1に見立てる 足すと→       10000           ↑         繰り上がり  となります。0001 が4ビットの1で、1111 が4ビットの−1です。問題は、二つを足して繰り上がった 1 ですが、5ビット目なので、「桁あふれ(overflow)」として“別扱い”にすれば、見事に 0000 即ち0となります。この方法だと、 :   : 0100  4 0011  3 0010  2 0001  1 0000  0 1111 −1 1110 −2 1101 −3 1100 −4 :   :  となって、普通に1ずつ増減させるだけで、数が負←→0←→正と連続して繋がりますし、正の数同士、負の数同士、正負の数同士、どの計算でも同じ計算回路で正しい結果となります。  さらに都合の良いことに、マイナスからプラスになる際にビット数以上に繰り上がる「桁あふれ」と、プラスからマイナスになる際にビット数より上から借りる「桁借り(borrow)」が有ったことを示すためには、CPUのビット数で表現できる数より大きい数の計算をする為に、計算で繰り上がった桁(あるいは上から借りた桁)を別扱いとして入れる「キャリー(carry)」というビットが、元々CPUに用意されているので、何も特別の回路を作る必要がありません。 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 参考:「桁あふれ」の出る計算  例1)4ビット二進数(最大値は15)で、30+24の計算 数を4ビットずつ2回に分けて計算する    上位 下位 二進数の30・・・11110 → 0001 1110 二進数の24・・・11000 → 0001 1000 これらを足す + ---- ----    0010 1 0110 ↓   1 ←「桁あふれ」ビットを上位に足す   + ---- 答えは54 ・・・110110 ← 0011 0110  例2)4ビットで、−7+12の計算 二進数4ビットの−7 ・・・ 1001 二進数4ビットの12 ・・・ 1100 足して答えは5    ・・・1 0101 ↑「桁あふれ」ビットは別扱い(無視する)  例3)4ビットで、−7+(−7)の計算 二進数4ビットの−7 ・・・ 1001 二進数4ビットの−7 ・・・ 1001 足して答えは−14  ・・・1 0010 ↑「桁あふれ」ビットを−の符号と見る −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  また、この負の数の表現方法なら「正の数を負の数にする」場合や「負の数を正の数にする」場合など、その数の「符号を反転する」場合(具体的には、4を−4にするとか、−12を12にするとか)も簡単で、ビットを全て反転してから1を足すのです。例えば 0001 なら、ビット毎に 0 と 1 を反転して 1110 にしてから 1 を足して 1111 です。これをコンピュータ用語で「2の補数(Two's Complement)」と言います(数学の補数とちょっと違いますよネ?)。最後に 1 を足さない場合は「1の補数」と言います(何で?(^^;))。  「2の補数」の例、4ビットの1の符号を反転して−1を作る場合 二進数の1    → 0001 ビットを反転   → 1110 ・・・1の補数 1を足すと−1  → 1111 ・・・2の補数 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 参考:補数とは?  広辞苑によると  1.自然数a、bについて、a+b=10のとき、一方を他方の10に対する補数という。余数。  2.n桁の二進数について、2のn乗−1−aを1に対するaの補数という。  だそうです。1番が言わば“10の補数”で、2番がコンピュータ関連で言うところの“1の補数”でしょうか? コンピュータ関連で言う“2の補数”を辞書風に言うと“CPUが一度に扱うビット数以下の二進数aに足すと1桁あふれて全ての桁が 0 になる二進数b”です。ちょっと不正確に分りやすく書くと“aに足すと丁度1桁上がる最小の数b”です。 4ビットの場合        4ビット二進数のa  → 0001  足すと桁あふれして0になる数b  → 1111・・・2の補数           足してみると   +----                    1 0000                    ↑ ↑                 桁あふれ 0 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−  余談ですが、先に「この(負の数の表現)方法で作った場合、正の数同士、負の数同士、正負の数同士、どの計算も同じ計算回路で正しい結果になります」と書きました。この回路は、足し算をする回路です。引き算は、負の数を足すのと同じ事ですから、足し算をする回路だけで出来ます。掛け算/割り算は、足し算/引き算を繰り返せば出来ます。なんと、コンピュータ(CPU)は、本質的には足し算しかしていないのです! −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 参考:乗除算をする回路  現代ではコンピュータの黎明期と比較して、超複雑な回路も簡単に作れるようになり、掛け算/割り算を高速な方法で行える仕組みを実装するCPUが殆ど(全部?)です。でも“足し算する回路”と“桁をずらす回路”を上手く使って実現している様です(今は違うのかな?) 例1)3×5の機械的計算 二進数4ビットの3 ・・・ 0011 二進数4ビットの5 ・・・ 0101 5の 1 が有る桁に3をずらして置いて、それを足す 5 0101 5の 1 が有る桁 * * 置いた3 0011 置いた3 0011 足す + ------ 答え15 1111  何のことはない、人間の十進数の筆算と同じです 例2)9÷4の機械的計算 二進数4ビットの9 ・・・ 1001 二進数4ビットの4 ・・・ 0100 9を4桁(ビット数)ずらし、1桁ずつずらし戻しては 4を引いて(つまり4の“2の補数”を足して)いく。 この筆算では、1段ずつ下から上の4を引く。 引けると 1 、引けないと 0 を順に並べていく (実際には、キャリーを保存して並べていく)。 除数4 0100 9を4桁ずらす 1001 (準備) 3桁ずらした9 1 001 引けないので 0 2 〃 10 01 引けないので 0 1 〃 100 1 引けたので  1 元の位置 0001 引けないので 0 余り 0001 上から並べると 0010 ↑が答え 答え 0010 余り 0001 で 十進数表記なら2余り1  次は、同じく割り算の「突き放し法」というやつです(?) 例3)9÷4の機械的計算 その2 例2と同じ要領で取り合えず引いて(補数を足して)、 負なら除数を足し戻す。負なら 0、正なら 1 を並べる (実際には、キャリーを保存して並べていく)。 除数4 0100 結果 9を4桁ずらす 1001 (準備) 1桁戻し引く 1 001 1101 001 負で 0 1桁戻し足す 1010 01 1110 01  負で 0 1桁戻し足す 1100 1 0000 1  正で 1 元の位置引く 0001 1101  負で 0 余り 0001 上から並べると 0010 ↑が答え  こういった方法(他)の乗除算をする回路を、大きな数も扱えるビット数で作って乗せるようです(?) −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ● コンピュータは、その数の正負を知らない  上述の通り「2の補数」という表現方法なら、負の数をコンピュータ上で表す際には“何も特別の回路を作る必要が無い”のでした。ということは、この方法を用いるコンピュータは、実際には、正の数と負の数を全然区別していないということです。このことを説明する為に、8ビットCPUの代表であるZ80を取り上げます。Z80は、1980年代に最も使用されたCPUで、CPUが計算する時の値を記憶する内部メモリー1つに付き8ビット記憶でき、メモリーとCPUが値をやり取りする線(データバス)が8本のCPUです。いや、だから要するに8ビットCPUです(^^;)。 8ビットとは? 二進数で8桁      1  1  1  1  1  1  1  1 十進表記で桁の重み 128 64 32 16  8  4  2  1 最大値は重みを全て足した255。正の数の最小値は桁が全て0の場合の0  CPUは、基本的には常にそのビット数全部を使って数値を扱います。8ビットCPUなら1を表す時でも 00000001 とします。8ビットで表せる数より大きい数を扱う場合は、00000000 00000000 と8ビットを2つ合わせて使います(16ビット)。その場合は65535が最大値です。通常の8ビットCPUは、1度に17ビット以上の数を扱えません。  さて、この節は「コンピュータは、その数の正負を区別していない」事を説明するのでした。数の正負というと、Z80には、何らかの演算をした結果が正か負かを、0 か 1 かで示す「サインフラグ(Sign flag)」という機能があります。これは、扱う数を「符号付き(signed number)」という特別の数と“見立てる”ときに使います。「符号付き」の数では、「最上位ビット」が正負の符号の役割をします。正の数は一番上の桁である「最上位ビット」が 0 、負の数は「最上位ビット」が1となるので、人間にも正負の判別がしやすいです(そのため一番上の桁 = 最上位ビットを「符号ビット」とも呼びます。)それ以外は普通の数と同じで、負の数の表現には前節で説明した「2の補数」を使います。 8ビットの「符号付き」の数 普通の数 符号付きの数 | 8ビット | | | 7ビット | | | | | | | | | | | | | | | | | | | 7 6 5 4 3 2 1 0 符 6 5 4 3 2 1 0 号 「符号付き」の数の場合、一番上のビットは正負の符号  「サインフラグ」のフラグは文字通りの「旗」の意味で、人が旗を立てて合図を送るように、CPUが演算した結果について何かを示すときに値が 0 か 1 に変化するものです。そして、演算した結果を「符号付き」の数として見た場合に正か負かを、0 か 1 かで示すのが「サインフラグ」です。  「えっ? CPUが“演算をした結果が正か負かを、0 か 1 かのフラグで示す”のなら、コンピュータは正か負かを、ちゃんと区別してるジャン?」ですよね。まあ、もうちょっと読んで下さい。  このZ80の「サインフラグ」は、実際には演算結果の8ビット数値の内の、最上位のビット値がそのままフラグに入るだけです。 例)最上位と最下位のビット(8ビットの場合)  最上位ビット(MSB = Most significant bit)  ↓  00000000         ↑         最下位ビット(LSB = Lowest significant bit)  「サインフラグ」は、この最上位ビットを、正負の符号に見立てて、「もし、この演算結果を“符号の付いた数”と見るのなら、一番上のビットが負の符合を表す 1 ですよ(正の符号で 0 ですよ)」と、フラグを立てたり降ろしたりして教えてくれるのです。  仮に、演算結果が 10000001 だった場合、この値が正の数で129なのか、負の数で−127なのか、判断しているわけではないのです。繰り返しますが、「サインフラグ」は「一番上のビットが立ってます(寝てます)。もし、この演算結果を“符号の付いた数”と見るのなら、一番上のビットが負の符合を表す 1 ですよ(正の符号で 0 ですよ)」と教えてくれるだけです。で、「もし、符号の付いた数として見る」のは、もちろんプログラム(を作る人)です。よってZ80CPU自身は、正負の断定はしていないのです。  「サインフラグ」= Z80さんからの連絡「演算結果が正か負かは知らないけどぉ、“符号付きの数”で符号を表す時に使う最上位ビットは立ってる/寝てる、よ」というわけです(^^;)。  他に、Z80には、演算結果がマイナスになったかどうかの判定に良く使われる「キャリーフラグ」というフラグもあって、8ビットまたは16ビットで演算した結果が9ビット以上または17ビット以上になった場合等の「桁あふれ」時と、8ビットまたは16ビットで演算する際にビット数より上の桁から「桁借り」が有った場合(他)に1となります(つまりキャリーフラグは、前節で述べた「桁あふれ」ビットの値を示します)。  例えば、何らかの減算の際にビット数より上の桁から「桁借り」があったということは、計算結果がマイナスになったということです。「桁借り」が有ったので「キャリーフラグ」が立ちます。が、「キャリーフラグ」が示すのはあくまで「桁借り」が有ったということであって、その結果の数値自体が正であるか負であるかを判断しているのではありません。 計算する → 結果マイナスになり「フラグ」も立つ → 正か負かは未定  「えっ、計算結果がマイナスなのに、それが負じゃない数なんて、意味不明だろ?」ですよね。そこが、コンピュータの世界です。次の節でこれを説明します。 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 参考:CPUにおける符号付きの数(Signed Binary)  CPU機能で演算する数を「符号付き」の数として見る(扱う)場合、CPUの一度の命令で使う数用のビット数(Z80なら8ビットか16ビット)で表現できる二進数の一番上の桁は数値ではなく「符号」なので、そのビット数で表現できる全部の数を、正の数、負の数、の2つに分ける事になり、表現できる数値が半分の大きさになります。仮に4ビットなら、通常の見方で表現できる値は 0000〜1111、即ち十進数の0〜15の16種類ですが、最上位ビットを符号として符号付きの数字と見れば、表現できる正の値は 0000〜0111、即ち0〜7、負の値は 1111〜1000、即ち−1〜−8となります。言葉では分かりにくいので表にすると  4ビットの符号なしの数と、符号付きの数  同じ 0 と 1 のビット列を  符号なしの数として見る 符号付きの数として見る  正の数と見る 負の数と見る ビット列  15 -1 1111 -1  14 -2 1110 -2  13 -3 1101 -3  12 -4 1100 -4  11 -5 1011 -5  10 -6 1010 -6   9 -7 1001 -7   8 -8 1000 -8   7 -9 0111 7   6 -10 0110 6   5 -11 0101 5   4 -12 0100 4   3 -13 0011 3   2 -14 0010 2   1 -15 0001 1   0 0000 0 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ● 正負の数の見なし方。それは貴方が決める  私の説明の至らなさや、寄り道した所も多くて、混乱された方も居られますでしょうし、ここで一度、今までの内容をまとめてみます。   0.機械に数を扱わせるには二進数が最適   1.コンピュータ(CPU)は全てを2値で符号化して二進数として扱う   2.CPUが計算する際には、正の数と負の数を特に区別しないで     全く同じ演算回路で扱っている   3.もしCPU自身に、扱う数に正負の符号を付けさせるなら、そのCPUの     ビット数の最上位の桁を 0 =正の符号、1 =負の符号とする  という事でした。そして、   4.CPUが何か計算した結果、値がマイナスになったとしても、     それが負の数か、あるいは正の数か、CPUでは区別していない  という事を今から説明するところでした。  コンピュータ自身が扱う数の正と負を区別しないなら、誰が区別するのでしょうか? サインフラグの所でチラッと述べたように、その答えはプログラム(を書く人)です。C言語やBASICなどの高級言語だと、ちゃんと言語側で負の数であるかないか(プログラムを書く時に)区別するように作られていますが、コンピュータ(CPU)の言葉である機械語や、それに最も近いアセンブリ言語(Assembly language)だと、その数は正か負かプログラマーが全て頭の中で把握しておかなくてはなりません。何しろ十進数とは違って、例えば 1111 が 正の数で15なのか、負の数で−1なのか、数字の見た目でも、コンピュータ内部でも、全く同じで区別が無いのですからネ。  アセンブリ言語上で数を負として扱う場合の区別の仕方 = 見なし方についてですが、そこには、コンピュータの仕組みが絡んできます。仮に8ビットで区切るなら、8ビットがとる値の範囲は0〜255ですから、8ビットの値を0から1ずつ増やしていけば255となり、255から更に1増やすと「桁あふれ」して0に戻ります。増やしていけば、0→255→0→255・・・と繰り返すわけです。  8ビットの値を0から1ずつ増やしていくと0→255→0→255・・・と繰り返す 00000000→00000001→00000010・・・11111110→11111111→00000000  逆に255から1ずつ減らしていくなら、255→0→255→0・・・と繰り返す 11111111→11111110→11111101・・・00000001→00000000→11111111  角度の0度と360度が同じ位置であるように、8ビットの数値は0を起点(基点?)にグルグルと輪の様に繋がっているのです。もしアセンブリ言語プログラマーがそれを負の数として扱うなら、その0→1→2と回る方向を“正の方向”と見なして、逆の0→255→254と回る方向を“負の方向”と見なすわけです。コンピュータの“負”とは「逆方向に進む」ことです。   同じ値をどう見なすか・・・   → 正の方向 → 起点 → 正の方向 → 11111110 11111111 00000000 00000001 00000010   ← 負の方向 ← 起点 ← 負の方向 ←  一見ややこしいように思いますが、0と255の様に「ビット数の取る最小値と最大値が繋がっている」点を無視して考えてみれば、負とは元の「逆方向に進む」というだけの事ですから、とても単純で実際的な正と負の概念の認識です(と思います)。  このことを、分かり易いよう十進数で書くと、        → 正の方向 → 起点 → 正の方向 → ・・・−4 −3 −2 −1  0  1  2  3  4・・・        ← 負の方向 ← 起点 ← 負の方向 ←  となって、この図を見ながら簡単な計算を考えてみると、 2−5は、正の2から、負の方向に5進む。結果は−3 −1+2なら、−1から正の方向に2進む。結果は1 1×3なら、0から正の方向に1の3回分進む。結果は3 −2×2なら、0から負の方向へ2の2回分進む。結果は−4 −1×−3なら、0から負の方向の負の方向へ1の3回分進む。結果は3 4÷−2なら、0から負の方向へ4から2を引ける回分進む。結果は−2  ね、見たまんまでしょ(^^;)? 足し算・引き算なら式の始めの数の位置から後の数の分だけ正か負の方向に移動する、掛け算・割り算なら0の位置から上の説明通りに移動する。ただし、現実世界と違うのは、先に書いた通りコンピュータにはビット数の制限 = 一度に扱える数の上限が有って、その為に「ビット数で取る最小値と最大値が繋がっている」点です。この「ビット数で取る最小値と最大値が繋がっている」点が、コンピュータにはとても重要で、だからこそ、前々節の「2の補数」という方法が使えるのです。  「最小値と最大値が繋がっている」8ビットの数を十進数で表すと、 同じ数を 正と見るか?      → 正の方向 →  起点  → 正の方向 → ・・・253 254 255 0    1    2    3 ・・・ 負と見るか?      ← 負の方向 ←  起点  ← 負の方向 ← ・・・−3  −2  −1  0  −255 −254 −253・・・  です。見方次第で255は−1であり、−1は255です。マイナスの値も正の方向の矢印に沿って計算すれば、−1+1は255+1で、0です。−1+2は、255+2で1です。1−3は、1から負の方向へ3歩ですから254で、この254を負と見れば−2です。同じ1−3を正の方向で見ると−3は253なので、1+253でグルッと回って、やっぱり254即ち−2です。これらの計算は、全て「ビット数で取る最小値と最大値が繋がっている」からできるのです。  前節で、Z80の「キャリーフラグ」の動き方について、計算時に「桁あふれ」や、ビット数以上から「桁借り」が有った場合にフラグが立つと言いました。これを上図を見ながら別の言い方をすれば、計算時に値が0と255の間を通ると、フラグが立つわけです。  255に2足せば1です。計算としては正の向きに0と255の間を超えた(桁あふれした)ので「キャリーフラグ」が立ちます。1から2を引くと、答えは−1です。計算としては負の向きに0と255の間を超えた(桁借りした)のでマイナスです、「キャリーフラグ」も立ちます。が、その結果の−1(二進数なら11111111)を負の数−1とみるか、正の数255と見るかは、プログラム(を書く人)しだいです。  注意しなければならないのは、これは「符号付き」の数ではない(「符号付き」の数と考えていない場合な)ので、最上位の符号ビットが 0 でも 1 でも関係ありません。CPU機能のサインフラグを使って判定することもできませんし、あくまでプログラム(を書く人)がその数を正と見なすか負と見なすかという事です。  前節に書いた「えっ、計算結果がマイナスなのに、それが負じゃない数なんて、意味不明だろ?」について、もうお分かりいただけたと思います。計算結果はマイナスでも、その数を正と見るか負と見るかは、プログラム(を書く人)しだいなのです。  今度はコンピュータっぽく二進数で簡単な計算をしてみましょう。  例)−2+10を、8ビットコンピュータっぽく計算する −2は、2のビットを反転して1足したものなので 8ビットコンピュータの中ではこういう数字である  二進数8ビットの2 → 00000010  反転する      → 11111101  1を足す      → 11111110 ←これが二進数8ビットの−2 別にコンピュータが(特に命令しなければ)こんな 操作をするのではなく、−2は初めから人間がこう 入力すればよい 11111110 この−2を正の数と見れば254である。正か負かは コンピュータ自身には関係ない そこに10を足す  二進数8ビットの−2 → 11111110 ←でも254とも見れる  二進数8ビットの10 → 00001010  足す          + --------  答えは8       →1 00001000              ↑           「桁あふれ」ビットは別扱い 必要なら、この答えの8を−248と見てもかまわない −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 参考:8ビットCPUの機能を使った符号付きの数  前記した通り、値を「符号付き」の数として考える場合は、正負を判定させるときに、前出の「サインフラグ」といった様な“CPUが持つ判定機能”を使えます。8ビット相当の十進数で考えると、  8ビット符号付の数の循環  ・・−4 −3 −2 −1  0  1  2  3  4・・ ・                              ・ ・                              ・  ・・・・・−127 −128←→127 126・・・・・・・  となります。この様にCPUの判定機能を利用する場合は、最小値と最大値の繋がる場所が0の位置ではなく、−128と127の間になります。数を足していって128、あるいは引いていって−129(16ビットで考えるなら32768、−32769)になると正負逆転して値が飛ぶので注意が必要です。正の方向なら127までは正で、128からは負です。負の方向なら−128までは負で、次は127に飛びます。 8ビット「符号付き」の数のグラフ 増やしていくと→    ←減らしていくと     127     127     127   /     /     /  /     /     / 0/     0/     0/      /      /     /      /    /      /    -128     -128  しかしこれも、CPUの判定ではそうなるだけで、プログラム(を書いた人)しだいで、正負どちらの値としても利用できるわけです。「符号付き」の数としてCPUに正負を判定させて、次にその同じ数を「符号なし」の数として扱っても構わない訳です。 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 参考:二進数の小数  十進数は1/10すると桁が下がります。小数点以下でも同じです。1の1/10が0.1で、0.1の1/10が0.01です。   1000─┐       10分の1    100←┤       10分の1     10←┤       10分の1      1←┤       10分の1    0.1←┤       10分の1   0.01←┤        :  二進数は1/2すると桁が下がります。小数点以下でも同じです。二進数1の1/2が0.1で、0.1の1/2が0.01です。   1000─┐        2分の1    100←┤        2分の1     10←┤        2分の1      1←┤        2分の1    0.1←┤        2分の1   0.01←┤        :  それだけの事なのですが、二進数のままでは人間に理解しづらいので、十進数に変換したい場合が多々有ります。  例)二進数の小数を十進数にする方法  方法その1.桁の重みを足す  二進小数     0. 1 1 1 1 1 1 1  十進で桁の重み   0.5 0.25 0.125 0.0625 0.03125 0.015625 0.0078125    例)二進数10.11を十進数にする                 1   0   0.1   0.01     十進での桁の重みを足す 2 + 0 + 0.5 + 0.25     答え          2.75  二進数の小数点以下の桁の重みを十進数で表すと半端になって、数が小さくなるほど端数が増えますので、二進数の小数点以下のどの桁が十進で幾らであるか、覚えるのが大変です。二進小数の桁の重みを足して十進小数にするのは、一見簡単にみえて、その実あまり上手くない方法かもしれません。  方法その2.整数と見なして十進数を求め、小数以下の桁数分2で割る    例)二進数10.11を十進数にする     整数と見なすと1011で、十進なら11     小数以下は2桁なので2×2=4     11÷4=2.75  答えは2.75  この方法なら、人間様にも比較的簡単に計算できそうです。逆に十進小数から二進小数に変換したい場合は、下記のようにします。    例)十進数の2.625を二進数にする     整数部と小数部に分ける 2    .625     小数部だけを2倍する      1.25         〃           0.5         〃           1.0     小数部が0になったので、 この列↑を上から並べると101     最初に分けた整数部を二進数にすると10で、小数部と組み合わせると     答えは10.101となる。  なお、十進小数から二進小数へは、綺麗に変換出来ない場合が多々有ります。例えば十進小数の0.1を幾ら2倍しても小数部は0になりませんので、二進変換すれば無限小数(循環小数)になってしまいます。このような二進数と十進数との“変換誤差”について、コンピュータでは最初から誤差が出ないようにBCD(Binary Coded Decimal)= 二進化十進数という数の表現方法を使って二進の計算回路上で十進法計算する場合が多いようです。  いずれにしてもコンピュータでは、計算で一度に扱える桁に限度がありますから、桁の大きな数はある程度で丸めてしまう=“丸め誤差”は必ず出るわけです。 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ● ちょっと脱線。コンピューターは人工無脳であるか?  前々節から何か計算の例を出す度に、「桁あふれ」ビットを上位に足すとか、別扱いとか、それも数えるのだとか、毎回違うことを書いていて、疑問に思われる方も多いと思います。実はこれもコンピュータ(CPU)のおバカなところで、その計算によって「桁あふれ」ビットの扱いを変えなければならないのに、それはプログラム(を書く人)に任されているのです。あるいは、各種類の計算用に一々別の回路を持つ数値計算専用のCPUを、CPU内に組み込んであったりします(よね?)。  そんな理由で、アセンブリ言語では、或る計算で使う値の範囲を予め全部想定して、「桁あふれ」ビットの扱い方も含めて、間違った計算をしないようにプログラミングします。高級言語だと、その言語を使う普通のプログラマーがそんなことを意識せずに数学っぽい数式を書いて計算して(できる限り)正しい結果が出るように、言語を作る偉い人が物凄く考えて作ってある訳です。  CPUは、その計算した数値が正か負かも知らないし、一々その時に的確に指示しなければ、往々にしてちゃんと足し算することすら出来ません。コンピュータは、優秀な知能も持っていなければ、愚か者でもありません。「♪〜良いも悪いもリモコン次第、鉄人、鉄人何処へ行く、ビューンと飛んでく鉄人28号」ならぬ、「良いも悪いもプログラマーしだい」な、単純明解な“機械”なのです。(えっ「貴方、年いくつなんですか?」って、いや確か1990年代後半の再放送ですよ、見たのは。) −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 脱線の脱線、そのアルゴリズムはホントに手順が少ないのか?  高級言語で「こうしてこうすると、手順が減って速いです。」と、アルゴリズムの入門書等に書いてあって、確かにその高級言語では、文量や手順が減っていて速そうなのですが、アセンブリ言語レベルで考えた時に、ホントに速くなるのかどうか、微妙な感じのって在るのです。その1文の条件判断を入れるのに、機械語ならこれだけの手間が要るなぁとか、メモリーを確保して読み書きするとか考えると、ホントに速くなってるのかなぁ? 下手すると遅くならないかぁ?って・・・  まあ、機械語の名人達人が高級言語を作るのでしょうから、私などが考えることすら及びもつかない、凄く効率的な方法で機械語を組んであるのでしょうし、それを理論通りに高級言語でプログラミングすれば、やっぱり速くなるのでしょうが、万一を考えて、アルゴリズムを考えた時はプログラム文や理論だけに寄らず、一々実際にコンパイルしてみて時間を計って比べた方が良いかもしれません。現行PCなら、少々のソース変更で速くなったかどうか、体感では分からない事も多いでしょうから、ちゃんとタイマーで計って比べてみるのです。  もしかすると、そういった意味での誤解が無いのは、アセンブリ言語だけかもしれません。 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ● まとめと、コンピュータによる悟り  まとめ   0.機械に数を扱わせるには二進数が最適   1.コンピュータ(CPU)は全てを2値で符号化して二進数として扱う   2.CPUが計算する際には、正の数と負の数を特に区別しないで     全く同じ演算回路で扱っている   3.もしCPU自身に、扱う数に正負の符号を付けさせるなら、そのCPUの     ビット数の最上位の桁を 0 =正の符号、1 =負の符号とする   4.CPUが何か計算した結果、値がマイナスになったとしても、     それが負の数か、あるいは正の数か、CPUでは区別していない   5.コンピュータ上の値の正負は、CPUが何と言おうが一々人間様が決める  たったこれだけの事を説明しようとして、随分とクドクドした長文に成ってしまいました。私も未熟よのう、フッフッフ。  「コンピュータの任意の数値は、プラスでありマイナスである。その数値がプラスであるかマイナスであるかは、プログラムを書く人が見なす」のでした。上手く考えたものです。考えたというか「あっ! マイナスなんて、そう見なしたらイイだけジャン」と「はたと気づいた」でしょうか? でも、同じ物が見方によって正負180度逆転するって、この人間世界そのものではないですか?  ワレワレ「奴らは悪魔の政権だから、偉大なるワレワレが爆撃して破壊する!」  私「えーと、悪魔の政権は爆撃される方ですか? 爆撃する方ですか?」  コンピュータ「任意です」  しかも、コンピュータの「ビット数で取る最小値と最大値は連続」していて「始まりの0から一歩一歩、最大値まで進んだら、また0に戻る」のです。  「驕る平家は久しからず」、ピラミッドを作ったエジプト王朝も、神聖ローマ帝国も、蒙古もナポレオンも、今は歴史の彼方です。日本に軍事独裁政権を建てた(というか、上手く政権を横取りした)徳川江戸幕府も、最後は一方的に西軍に敗れ去りました。  栄華を極めたかに見えた後は、真っ直ぐに、急速に衰退の坂道を下り、最後に壊滅してしまうのです。これから逃れた人間の歴史は未だ有りません。今現在、繁栄していると驕り高ぶった振る舞いをしている連中は、明日の敗者でしょう。そして、また新しい世界が興こるのです。月は満ちて必ず欠け、また新たな月が満ち、太陽は昇ると必ず沈み、また新たな陽が昇るのです。  コンピュータで扱う正と負の数を考えるだけで、こういう事が見えてくる・・・何事も一心に一筋に勤めれば、知らずに極め、おのずと道を「悟る」ように、コンピュータの言葉 ≒ アセンブリ言語を極めることは、この世の悟りを開く事なのかも知れませんネ。(そうなのか(^^;)?)  この文章の参考にした本:『Z80マシン語秘伝の書』日高 徹 著              『PROGRAMING THE Z80 THIRD EDITION』Rodnay Zaks 著  * おことわり *  この文章では、便宜的にプログラミング言語のアセンブリ言語(Assembly language)をCPUの言葉である機械語と同じものとして説明させていただきました。CPUの言葉 = 機械語の命令は、例えば8ビットCPUですら2進数を8桁から32桁も並べた符号なので、人間には凄く読み辛く、普通はとても覚えきれません。それを短い英単語風にしたものがアセンブリ言語で、機械語の命令に1対1で対応しています。  例、Z80CPUでAレジスタ(CPU内の記憶メモリの一つ)に10を足す命令    機械語    ・・・ 11000110 00001010    アセンブリ言語・・・ ADD A,10  機械語そのままよりは、遥かに覚えやすいでしょ? 私が考えたんじゃないけど 余談:「アセンブリ言語」と、謎の言葉“アセンブラ言語”  IPA(情報処理推進機構)の基本情報処理技術者試験にCOMETUという架空のコンピュータ用のCASLUという言語を使うものがあります。「試験で使用する情報技術に関する用語・プログラム言語など」というIPAの試験資料の「別紙1 アセンブラ言語の仕様」によると「CASLUはCOMETUのためのアセンブラ言語である」とあります。これ、誤解と混乱を招いていると思います。  CPUの機械語に一対一で対応した「言語」について、私の読んだ4冊のZ80に関する洋書、広辞苑や英語の辞書、ネットの検索でも出てくるのは「Assembly language」ばかりです。その英語の音を使って日本語に訳すなら「アセンブリ言語」でしょう。つまり、CPUの機械語に一対一で対応した「言語」をプログラミング言語の一種として一般化して呼ぶのなら「アセンブリ言語」です。当然に意図する意味も正確で、日本人に言い辛い/意味的に理解し辛いわけでもありません。  実際のところ、JISが“アセンブラ言語”という用語を使っていて、IPAの試験もそれに準拠しているそうで、またCASL以外でもCPUの機械語に一対一で対応した「言語」を“アセンブラ言語”とする入門書は“少数派”ながら存在していますし、かつて海外で“Assembler language”という言い方をした人や本があったのかもしれませんが、上記の通り“Assembler language”は、現在ネット検索しても出てこない言葉です。  コンピュータの世界で Assembler というのは「Assembly language」を機械語に翻訳するソフトの事で、「Assembly language」つまり「アセンブリ言語」は、アセンブラが無くとも人力で機械語に翻訳できますし、C言語とCコンパイラが別物であるように、プログラミング言語とそれで書いたソースを機械語に変換するソフトは別物ですからネ。  というわけで、政府の機関が“アセンブラ言語”という誤解を招く用語を用いているのはいかがなものでしょうか?  (さらに資料にある“プログラム言語”という言い方も、原語は「programming language」なので、音訳の日本語なら「プログラミング言語」ですよネ? もしかすると1960〜70年代の怪しい音訳(というか和製英語?)をそのまま使っているとか??)