2進数・16進数の変換と2の補数。この分野のいいところは、問題の条件を確認して手順を固定すれば、安定して同じ答えにたどり着けることです。多くの問題は決まった手順で処理できます。
ただし手順の前に、確認すべきことが3つ。ビット長は何ビットか、符号付きか符号なしか、整数か小数か。ここを飛ばすと、手順が正しくても答えがズレます。今日はこの確認込みで、手順を置いていきます。
まず原理:各桁は「桁の値 × 基数の累乗」
n進数の値は、各桁について「桁の値 × nの桁位置乗」を合計したものです。桁位置は小数点のすぐ左が0乗で、左へ行くほど+1、小数点の右は −1乗、−2乗…になります。この1個の原理で、2進も8進も16進も、全部同じ考え方で読めます。
手順1:2進数→10進数は「重みを足す」
2進数の各桁には重みがあります。右から 1、2、4、8、16、32、64、128。1が立っている桁の重みを足すだけです。
例題:2進数の100110を10進数に。
1が立っているのは、32・4・2 の位置。
32 + 4 + 2 = 38
式で書くと 1×2^5+0×2^4+0×2^3+1×2^2+1×2^1+0×2^0 = 38。さっきの原理そのままですね。
小数も理屈は同じで、小数点の右は 0.5、0.25、0.125、0.0625…と半分ずつになっていきます。0.101なら 1×2^-1+0×2^-2+1×2^-3 = 0.5+0.125 = 0.625。「小数第1位が2分の1」だけ覚えておけば、あとは半分にしていくだけです。
手順2:10進の非負整数→2進数は「2で割って余りを下から読む」
この手順が使えるのは非負の整数部分です。負数はまず絶対値を変換してから、指定されたビット長と負数表現(後述の2の補数など)に従います。小数部分は次の手順3で。
例題:10進数の38を2進数に。
38÷2=19 余り0
19÷2=9 余り1
9÷2=4 余り1
4÷2=2 余り0
2÷2=1 余り0
1÷2=0 余り1
余りを下から読んで 100110
「下から読む」のを忘れて上から読むと、きれいに逆順の別の数ができあがります。この逆順の値、典型的な誤答としてよく置かれています。
手順3:10進小数→2進小数は「2倍して整数部分を上から読む」
整数と方向が逆なのがポイントです。小数部分を2倍して、出てきた整数部分を上から並べます。
例題:0.625を2進数に。
0.625 × 2 = 1.25 → 整数部分 1
0.25 × 2 = 0.5 → 整数部分 0
0.5 × 2 = 1.0 → 整数部分 1(小数部分が0になったら終了)
上から読んで 0.101
さっきの逆方向(0.101→0.625)とちゃんと往復できていますね。「整数は割って下から、小数は掛けて上から」。セットで覚えてください。
補足:きれいに終わらない小数もある
10進数の0.1を同じ手順にかけると、小数部分が0にならず永遠に循環します。つまり2進数では有限桁で表せません。コンピュータは有限ビットで近似して保存するので、ここで丸め誤差が生まれます。浮動小数点の誤差問題の根っこはこれです。今は「0.1は2進数で割り切れない」だけ持って帰ってください。なお、問題文で「小数部は◯ビットまで」と指定されたら、指定桁まで変換して、切り捨て・四捨五入など問題文の丸め指定に従います。指定がないのに自己判断で丸めないこと。
16進数は「2進数4桁のあだ名」だと思えばいい
16進数が別世界に見える人は多いんですが、正体は2進数を4桁ずつ束ねてあだ名をつけたものです。16=2^4なので、16進1桁と2進4桁が一対一に対応します。
| 16進 | 2進 | 16進 | 2進 |
|---|---|---|---|
| 0 | 0000 | 8 | 1000 |
| 1 | 0001 | 9 | 1001 |
| 2 | 0010 | A | 1010 |
| 3 | 0011 | B | 1011 |
| 4 | 0100 | C | 1100 |
| 5 | 0101 | D | 1101 |
| 6 | 0110 | E | 1110 |
| 7 | 0111 | F | 1111 |
10進数との変換は、桁の重みが16になるだけ。
例題:16進数のB7を10進数に。
Bは11。11 × 16 + 7 = 176 + 7 = 183
逆方向は「16で割って余り」。200なら 200÷16=12余り8、12はCなので C8。手順1・2の「2」が「16」に変わっただけです。
2進数⇔16進数は表引きで直行できる
10進数を経由しなくても、対応表で直接変換できます。
- 2進→16進:小数点を基準に、整数部は右から左へ、小数部は左から右へ4ビットずつ区切る。足りない分の0は、整数部なら左側、小数部なら右側に補う。例:10110110 → 1011|0110 → B6。小数付きなら 1011.01 → 1011.0100 → B.4(小数部の右に0を2つ補ってから区切る)
- 16進→2進:各桁を4ビットに開くだけ。例:3A → 0011|1010 → 00111010。固定ビット長の指定がない「数値としての表記」なら先頭の0を省略して111010でもOKですが、「8ビットで答えよ」のような固定幅指定があるときは、先頭0も表現の一部なので00111010の8桁を維持します
補足:8進1桁は2進3桁
8=2^3なので、8進数は3ビットずつの束です。110101なら 110|101 → 65(8進)。試験の主役は2進・10進・16進ですが、「16進は4ビット、8進は3ビット」とセットで覚えておくと取りこぼしません。
手順4:2の補数は「ビット長を確認 → 全部ひっくり返して+1」
負の数の表現でおなじみの2の補数。作り方は機械作業ですが、作る前に必ずビット長を確認してください。反転は「指定された固定ビット長の全ビット」が対象なので、ビット長が変わると答えのビット列も変わります。
例題:8ビットの2の補数で −5 を表すと?
①まず +5 を8ビットで書く:00000101
②全ビット反転:11111010
③1を足す:11111011
同じ−5でも、16ビットなら 1111111111111011 です。「何ビットで表すか」を問題文で確認して最初に丸をつけてください。指定が見当たらないときは、前後の条件や選択肢のビット数から読み取ります。
特に注意したいのが③の「+1忘れ」。反転しただけの11111010(これは1の補数といいます)が、典型的な誤答としてよく置かれています。「反転して、プラスワン」。合言葉にして手順を定着させてください。
なおこの反転は指定された固定ビット長の全ビットが対象で、+1で左端からあふれた桁は固定幅の外に捨てます。最小値−2^(n−1)(8ビットなら−128)にも、この手順を適用できます。ただし、絶対値の128は8ビットの符号付き正数としては表現できないため、ここでは絶対値128を8ビットの符号なしビット列10000000として置きます。これを全ビット反転すると01111111、さらに1を加えると10000000となります。つまり、−128では処理前の絶対値のビット列と、処理後の2の補数表現が同じになる点が特徴です。一般式では「nビットで−xを表すビット列の符号なし整数としての値=2^n−x」であり、2^8−128=128=10000000です。同じ10000000でも、符号付き2の補数として読めば−128、符号なし整数として読めば128になります。
ビット列を読むときは「符号付きか」をまず確認
逆方向(ビット列→10進数)で大事なのがここ。最上位ビットが1なら負数、と言えるのは、そのビット列を固定長の符号付き2の補数として読む場合だけです。同じ8ビットの11110000でも、
- 符号なし整数として読めば:240
- 符号付き2の補数として読めば:−16
問題文の「符号なし/符号付き」の指定で答えが変わります。符号付きで負数だったときの読み方は、作るときと同じ手順の逆再生。①全ビット反転 → ②1を足す → ③出てきた絶対値にマイナスをつける、です。
別解として、8ビットなら最上位桁だけ重みを−128にして足す方法もあります。11110000なら −128+64+32+16 = −16。検算に便利です。
なぜ2の補数を使うのか
作り方だけ覚えても味気ないので、理由をひとつ。2の補数を使うと、引き算を足し算に変換できるんです。固定ビット長では「A − B = A +(Bの2の補数)」が成り立ちます。
例:4ビットで 5 − 3 を計算する。
5:0101、3:0011
3の2の補数:1101
0101 + 1101 = 1 0010
固定4ビットからあふれた左端の1は捨てる → 0010 = 2
引き算回路を作らなくても、足し算回路1つで済む。しかも2の補数は0の表現が1つだけなので、nビットの2^n通りを無駄なく使えます。加算回路で減算も処理できて0の表現が一意——これが、2の補数が広く使われる理由のひとつです。なお固定nビットの計算では、nビットを超えた桁は捨てて処理します。数学的な結果が符号付きの表現範囲を超える場合は、後述のオーバーフローが別途発生します。
表現できる範囲
| 解釈 | nビットの範囲 | 8ビットの場合 |
|---|---|---|
| 符号なし整数 | 0 〜 2^n − 1 | 0 〜 255 |
| 符号付き2の補数 | −2^(n−1) 〜 2^(n−1) − 1 | −128 〜 127 |
ビット列の総数はどちらも2^n通り(8ビットなら256通り)で、それをどう割り振るかの違いです。符号付きのプラス側だけ127止まりなのは、0がプラス側の席をひとつ使っているから。「−127〜127」でも「0〜255」でもない点に注意です。
符号付き2の補数のビット数を増やすときは「符号拡張」
8ビットの値を16ビットに広げるときは、最上位の符号ビットを左へ繰り返します。
8ビットの−5:11111011 → 16ビット:1111111111111011(左を1で埋める)
8ビットの+5:00000101 → 16ビット:0000000000000101(左を0で埋める)
負数の左側を単純に0で埋めると、元とは異なる正の値として解釈されてしまいます。「符号ビットのコピーで埋める」です。
一方、符号なし整数を広げるときは話が別で、左側を0で埋めるゼロ拡張を使います。8ビット符号なしの11111011(=251)を16ビットにするなら 0000000011111011。符号なしの値に最上位の1を複製すると値が変わってしまうので、「符号拡張は符号付き2の補数用、符号なしはゼロ拡張」と使い分けてください。
BCD:「10進数字を4ビットずつ」の別方式
もうひとつ、混同しやすいのがBCD(2進化10進数)です。ここでは一般的な8421 BCD(各10進数字を、そのまま4ビットの2進表現に置き換える方式)を扱います。0000〜1001が数字の0〜9に対応し、1010〜1111は1桁の10進数字としては使いません。
57をBCDにすると、5→0101、7→0111 なので 0101 0111。一方、普通に2進数へ変換すると 111001。どちらも同じ10進数57を表していますが、表現方式とビット列が異なるわけです。「1桁ずつバラして4ビット」と唱えてください。
ちなみにシラバス用語の「パック10進数」は、各10進数字を4ビット(ニブル)ずつ詰めて格納する方式です。代表的な符号付き形式では最後の4ビットを符号に使うため、通常のバイトには数字が2桁入りますが、最終バイトは「数字1桁+符号」になります。+57なら 0000 0101 0111 1100(0x057C)、−57なら0x057D、が代表的な形です(符号コードや格納方式はシステムによって異なるので、詳細は問題文の指定に従ってください)。
シフト演算:「2倍・半分」には条件がつく
「左に1ビットで2倍、右に1ビットで半分」とよく言われますが、これは条件付きです。分けて整理します。
論理左シフト:非負整数で、上位からあふれるビットがなく表現範囲を超えない場合に、kビットで2^k倍になります。固定ビット長であふれると話が変わります。たとえば8ビット符号なしの11000000(=192)を1ビット左シフトすると10000000、8ビットの世界では128であって384ではありません。あふれた瞬間、「2倍」は成立しなくなります(固定nビットでは、実質的に2^nで割った余りが残る、という動きです)。符号付きの場合はさらに、シフト後のビット列を読んだときに符号が変わっていないかも確認してください。
論理右シフト:空いた左側を0で埋める方式。符号なしの非負整数なら、kビットで「2^kで割った整数部分」になります。
算術右シフト:空いた左側を符号ビットで埋める方式。符号を保ったまま桁をずらせます。
例題:11110000を右へ3ビットシフトすると?
論理右シフト:00011110
算術右シフト(8ビット符号付き2の補数の−16として):11111110 = −2
算術のほうは −16÷8=−2 と対応していて、意味が通っていますね。ここを取り違えて逆の選択肢を選ぶのが代表的なミスです。
もうひとつ、負の奇数では要注意。−5(11111011)を1ビット算術右シフトすると11111101、これは−3です。「−5の半分は−2.5だから切り捨てて−2」という直感とはズレます。算術右シフトのkビットは「2^kで割って負の無限大方向へ丸めた値」に対応するので、−2.5は−3になるわけです。正の整数なら通常の整数除算と同じ結果なので、この差が出るのは負の奇数のとき。負数のシフトは「半分」と暗算せず、ビット列を実際に動かしてください(プログラミング言語固有の演算規則を問う問題なら、その言語仕様が優先です)。
2進数の加算とオーバーフロー
演習で2進加算も出すので、規則だけ置いておきます。
0+0=0、0+1=1、1+0=1、1+1=10(繰り上がり)、1+1+1=11
例:1011 + 1101 = 11000。繰り上がりを丁寧に書けば筆算と同じです。
符号付きのオーバーフロー
固定ビット長では、数学的な結果が表現範囲を超えると、そのままではビット列で表せなくなります。8ビット符号付き2の補数で、
01111111(127)+ 00000001(1)= 10000000
10000000は−128。本来の答え128は8ビット符号付きでは表現できないので、これがオーバーフローです。判定の合言葉は「同じ符号どうしを足したのに、結果の符号が反対になったらオーバーフロー」。正+正が負、または負+負が正になったらアウトです。逆に、異符号どうしの加算ではこの形のオーバーフローは起きません。最上位から繰り上がり(キャリー)が出たかどうかだけでは符号付きのオーバーフローは判定できないので、符号の反転で見てください(符号なし整数の場合はキャリーアウトの扱い自体が別の話になります)。この判定は符号付き2の補数の加算に対するものです。
ありがちなミスまとめ
| やらかし | 出てくる誤答 | 対策 |
|---|---|---|
| 余りを上から読む | 逆順のビット列 | 整数の余りは「下から」を指差し確認 |
| 10進小数にも割り算を使う | 手が止まる | 小数は「2倍して整数部を上から」 |
| ビット長を確認しない | 補数のビット列が定まらない | 最初に8ビット・16ビット等に丸をつける |
| 2の補数で+1を忘れる | 1の補数(1だけズレた値) | 「反転して、プラスワン」 |
| 符号の指定を見ない | 240と−16の取り違え | 符号なし/符号付きをまず確認 |
| BCDと通常の2進を混同 | 111001と01010111の取り違え | BCDは10進1桁ずつ4ビット |
| 左シフトのあふれを無視 | 単純な2^k倍の値 | 固定幅と捨てられる上位ビットを確認 |
| 負数の算術右シフトを暗算 | 負の奇数で1ズレ | ビット列を実際に動かす |
| 符号付きオーバーフロー見落とし | 正+正が負のまま採用 | 二数と結果の符号を比較 |
| 16進A〜Fの値ミス | 1ズレた値 | A=10から指を折って数えてOK |
| −128の絶対値128を符号付き8ビットで表そうとする | 正の表現範囲を超えて混乱する | 128を符号なし8ビット列10000000として置き、反転して1を加える |
| 固定8ビット指定なのに先頭0を省略 | ビット長不足 | 固定幅では先頭0も表現の一部 |
| 符号なし整数を符号拡張する | 値が変わる | 符号なしはゼロ拡張(左を0埋め) |
| パック10進数を常に1バイト2桁と考える | 符号ニブルの見落とし | 最終ニブルの符号を確認 |
| 算術右シフトを0方向の切り捨てと考える | 負の奇数で1ズレ | 負の無限大方向への丸め |
| 2進小数の打ち切り方を自己判断 | 最終ビットがズレる | 問題文の丸め指定を確認 |
仕上げに演習10問どうぞ
演習では、基数変換の各方向、BCD、2進数どうし・16進数どうしの加算、論理シフト、表現範囲、3桁固定(先頭0あり)の16進コードの表現数(16³=4,096とおり。「100〜FFFの3桁の数」と数えるなら15×16²=3,840とおりで別物です)など、代表的な問題を確認できます。手順と「ビット長・符号の確認」が身についたかのチェックにどうぞ。
今日のまとめ
- n進数は「各桁の値×基数の累乗」で読む。整数の10進→2進は「割って余りを下から」、小数は「2倍して整数部を上から」(有限にならない場合は問題文の丸め指定に従う)
- 16進1桁=2進4桁、8進1桁=2進3桁。表引きで直行できる。0埋めは整数部が左・小数部が右
- 固定ビット長の指定があるときは、先頭0も表現の一部
- 2の補数は「ビット長を確認して、反転して、プラスワン」。範囲は −2^(n−1) 〜 2^(n−1)−1
- 8ビットの−128は、絶対値128を符号なし8ビット列10000000として置き、反転して1を加えても10000000になる
- 最上位の1が負数を意味するのは「符号付き2の補数として読む」場合だけ。符号の指定をまず確認
- 符号拡張は符号付き2の補数用(符号ビットを複製)。符号なしはゼロ拡張
- BCDは10進1桁ずつ4ビット。パック10進数は最後のニブルが符号になる形式がある
- シフトの「2倍・半分」は条件付き。算術右シフトは負の無限大方向に丸まる(負の奇数で1ズレる)
- 同符号の加算で符号が反転したらオーバーフロー(異符号の加算では起きない)
次回は「MIPSと実効アクセス時間」。CPU性能計算の基本手順を整理します。
参考資料
- IPA「基本情報技術者試験(レベル2)シラバス Ver.9.2」
本記事の例題は、基本情報技術者試験の出題範囲及び標準的な数値表現を参考に、当サイトで独自に作成したものです。

コメント