ビット列は何を表す?——2進数・補数・文字コードと桁あふれ

同じ0と1の並びでも、桁数と解釈の規則によって数値や文字が変わる。位取り、2の補数、符号付き加算、UTF-8を具体的に読み解く。

この単元の目標
  • 2進数の位取りから10進数への変換を説明し、数値と固定長のビット列を区別できる
  • 指定したビット数で2の補数を作り、符号付き整数の範囲を求められる
  • 符号なしの桁あふれと2の補数での符号付きオーバーフローを区別できる
  • 文字、符号位置、符号化されたバイト列を区別し、UTF-8のデータ量を数えられる
📑 目次
  1. 1. 数と、数を表す記号列は同じではない
  2. 2. 位取りから2進数を読む
  3. 3. 負の数は、桁数を固定して約束する
  4. 4. 繰り上がりとオーバーフローを分ける
  5. 5. 文字も「解釈の規則」で表す
  6. 6. 練習:まず解釈とビット数を指定する
  7. 根拠とこのレッスンの範囲

1. 数と、数を表す記号列は同じではない

数の十三は、10進数では13、2進数では1101と書けます。表記が変わっても数量は同じです。一方、記憶装置にある「1101」という4ビットの並びは、それだけでは数量を確定できません。符号なし整数として読むなら13、4ビットの2の補数として読むなら−3です。ビット列+桁数+解釈の規則をセットにして初めて意味が決まります。

この記事では1バイト=8ビットとします。ビット列は左を上位、右を下位として書き、特記しない数値は10進数です。複数バイトをメモリに置く順序(エンディアン)は扱いません。固定長の計算例は整数だけを対象とし、小数や浮動小数点数とは区別します。

2. 位取りから2進数を読む

2進数の各桁には0または1が入り、右端から1、2、4、8、16、…の重みがあります。nを正の整数、各桁 bᵢ を0または1とすると、符号なし整数Uは次の和です。nビットには2ⁿ通りの並びがあり、0も含めるので最大値は2ⁿ−1です。

U=∑i=0n−1bi2i,0≤U≤2n−1U=\sum_{i=0}^{n-1}b_i2^i,\qquad 0\le U\le 2^n-1
例題1:45を8ビットにする

10進数45を8ビットの符号なし整数で表し、その並びを16進表記にも直そう。

解答・解説

45=32+8+4+1なので、8ビットでは **00101101**。左から4ビットずつ区切ると0010と1101で、16進表記は **2D**(D=13)。2×16+13=45でも検算できます。先頭の00は数値には影響しませんが、8ビットで保存するという条件を示します。

逆に10進数から変換するときは、2で割った余りが下位の桁になります。45÷2=22余り1、22÷2=11余り0、11÷2=5余り1、5÷2=2余り1、2÷2=1余り0、1÷2=0余り1。余りを最後から読むと101101です。16進数はビット列を短く書く表記であり、16進に直すだけでデータが圧縮されるわけではありません。

3. 負の数は、桁数を固定して約束する

ここでは符号付き整数に2の補数表現を使います。最上位ビットの重みだけを負にし、残りは通常の位取りで読みます。n≥2では、最上位が0なら非負、1なら負となり、範囲は−2ⁿ⁻¹から2ⁿ⁻¹−1です。符号ビットを取り除いた残りが絶対値、という読み方ではありません。

S=−bn−12n−1+∑i=0n−2bi2i,−2n−1≤S≤2n−1−1S=-b_{n-1}2^{n-1}+\sum_{i=0}^{n-2}b_i2^i,\qquad -2^{n-1}\le S\le 2^{n-1}-1
4ビットの並び符号なし整数として読む2の補数として読む
000000
011177
10008−8
110113−3
111115−1

正の整数xを負にした−xの並びは、nビットのxの0と1をすべて反転し、1を加えて下位nビットを取ることで作れます。これは符号なしで見た値を2ⁿ−xにする操作です。反転だけでは1の補数であり、2の補数ではありません。xと2ⁿ−xの和は2ⁿとなり、下位nビットがすべて0になるため、加算で打ち消し合います。

例題2:8ビットで−18を表す

8ビットの2の補数表現で−18を作り、負の重みを使って検算しよう。

解答・解説

18は00010010。反転すると11101101、1を足すと **11101110**。−128+64+32+8+4+2=−18です。符号なしとしてなら238ですが、符号付きとして読むと238−256=−18になります。

8ビットの2の補数の範囲は−128〜127です。10000000(−128)を反転して1を足しても下位8ビットは10000000となりますが、これは+128を表すことにはなりません。+128が範囲外だからです。負数を桁数だけ増やして同じ値に保つときは符号拡張を使い、例えば4ビットの1101(−3)は8ビットで11111101とします。00001101では別の値です。

4. 繰り上がりとオーバーフローを分ける

以下は「加算後に下位nビットだけを残す」という計算モデルです。符号なし加算では、最上位の外への繰り上がりがあると数学的な和が表現範囲を超えています。一方、2の補数の符号付き加算では、同符号の2数を足したのに結果の符号が変わったときにオーバーフローが起きます。異符号の2数の和は、両方が元の範囲内なら範囲を超えません。

例題3:0111+0001は正しい加算か

4ビットで0111+0001=1000となった。符号なしの場合と2の補数の場合を別々に評価しよう。

解答・解説

符号なしでは7+1=8で、0〜15の範囲内なので正しい結果です。2の補数では7+1の数学的な和8が−8〜7の範囲外で、1000は−8と読まれます。**外への繰り上がりはなくても符号付きオーバーフローは起きます。**

例題4:1111+0001で捨てる1

4ビットの1111と0001を足すと、桁を制限しない筆算では10000となる。下位4ビットを残した0000をどう解釈するか。

解答・解説

符号なしなら15+1=16は範囲外で、残る0は数学的な和ではありません。2の補数なら−1+1=0なので、0000は正しい結果です。外へ繰り上がった1があるだけで符号付きオーバーフローとは判定できません。

実際のプログラミング言語では、範囲を超えたときにエラーにする、より広い型で計算する、下位ビットを残すなど、型や言語により規則が違います。ここでの固定長モデルを、すべての言語の整数演算にそのまま当てはめてはいけません。

5. 文字も「解釈の規則」で表す

文字コードという言葉は広く使われますが、学習では「文字に番号を対応させること」と「その番号をバイト列にすること」を分けましょう。Unicodeの符号位置はU+0041のように16進数で書きます。UTF-8はUnicodeの符号位置をバイト列へ符号化する方式の一つです。ASCIIは7ビットの符号で、ASCII範囲の文字はUTF-8でも同じ値の1バイトになります。

文字Unicode符号位置UTF-8のバイト列(16進)バイト数
AU+0041411
1U+0031311
あU+3042E3 81 823

数値の1を8ビットの符号なし整数として保存するなら00000001です。しかし文字「1」をUTF-8で保存するなら00110001(16進31、数値として読めば49)です。また、文字列「13」はUTF-8で31 33という2バイトであり、数値13の8ビット表現00001101とは違います。表示が似ていても、数値への変換と文字列の連結は別の操作です。

例題5:文字数とバイト数

自作の短い文字列「Aあ1」をUTF-8で保存する。BOM、改行、終端文字などの付加情報は含めない。何バイト、何ビットになるか。

解答・解説

表からバイト列は41 E3 81 82 31で、1+3+1= **5バイト=40ビット**。この例の文字は3個ですが、文字数をそのままバイト数にはできません。「あ」が3バイトだからといって、すべての日本語の文字が常に3バイトになるとも限りません。

UTF-8ではUnicodeのスカラー値を1〜4バイトで表します。ただし、見た目の1文字が結合文字や絵文字の組合せなど複数の符号位置からなる場合があります。「1文字=最大4バイト」という見た目の文字単位の断定はできません。文字化けを避けるには、保存側と読み取り側で符号化方式を一致させる必要があります。

6. 練習:まず解釈とビット数を指定する

筆算と説明
  1. 8ビット列10100110を、符号なし整数と2の補数の両方で読もう。

    解答・解説

    符号なしは128+32+4+2=166。2の補数は−128+32+4+2=−90です。同じ並びでも最上位の重みが異なります。

  2. 8ビットで−37を2の補数表現にせよ。

    解答・解説

    37は00100101。反転して11011010、1を足して11011011です。符号なしの値219から256を引くと−37で検算できます。

  3. 8ビットの2の補数で01100100と00110010を足す。数学的な和、残るビット列、その解釈を示し、オーバーフローを判定せよ。

    解答・解説

    100+50=150ですが、範囲は−128〜127です。残る10010110は−128+16+4+2=−106。同符号の正数同士から負になっており、符号付きオーバーフローです。符号なしなら150で範囲内です。

  4. 文字列「11」をUTF-8で保存する場合と、数値11を8ビット符号なし整数として保存する場合の違いを答えよ。付加情報は無視する。

    解答・解説

    文字列は16進31 31で2バイト(16ビット)。数値は00001011で1バイト(8ビット)です。文字を二つ並べることと、その表記を数値に変換することは異なります。

  5. 4ビットの2の補数1110を、値を変えずに8ビットへ拡張せよ。

    解答・解説

    11111110です。最上位の1を補う符号拡張により、−2を保ちます。00001110だと+14になってしまいます。

確認クイズ

問1. 8ビット列11111111について正しい説明は?

問2. 8ビットの2の補数で表せる整数の範囲は?

問3. 4ビットの2の補数加算で、符号付きオーバーフローが起きるのは?

問4. UTF-8について正しい説明は?

根拠とこのレッスンの範囲

整数部分は位取りと2ⁿを法とする計算から導出した架空の固定長モデルです。文字符号化の根拠はRFC 3629(2003年11月)、特に第3節「UTF-8 definition」と第6節のBOMの説明です。https://www.rfc-editor.org/rfc/rfc3629 。本記事はそのUTF-8の定義を対象とし、Unicodeの収録文字数や特定OSの既定文字コードは述べません。文字の対応例はNodeのTextEncoderでも検算しています。

📝 メモこの端末のみに保存
メモ欄はページ読み込み後に使えるようになります。