ビット列は何を表す?——2進数・補数・文字コードと桁あふれ
同じ0と1の並びでも、桁数と解釈の規則によって数値や文字が変わる。位取り、2の補数、符号付き加算、UTF-8を具体的に読み解く。
- 2進数の位取りから10進数への変換を説明し、数値と固定長のビット列を区別できる
- 指定したビット数で2の補数を作り、符号付き整数の範囲を求められる
- 符号なしの桁あふれと2の補数での符号付きオーバーフローを区別できる
- 文字、符号位置、符号化されたバイト列を区別し、UTF-8のデータ量を数えられる
📑 目次
1. 数と、数を表す記号列は同じではない
数の十三は、10進数では13、2進数では1101と書けます。表記が変わっても数量は同じです。一方、記憶装置にある「1101」という4ビットの並びは、それだけでは数量を確定できません。符号なし整数として読むなら13、4ビットの2の補数として読むなら−3です。ビット列+桁数+解釈の規則をセットにして初めて意味が決まります。
2. 位取りから2進数を読む
2進数の各桁には0または1が入り、右端から1、2、4、8、16、…の重みがあります。nを正の整数、各桁 bᵢ を0または1とすると、符号なし整数Uは次の和です。nビットには2ⁿ通りの並びがあり、0も含めるので最大値は2ⁿ−1です。
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です。符号ビットを取り除いた残りが絶対値、という読み方ではありません。
| 4ビットの並び | 符号なし整数として読む | 2の補数として読む |
|---|---|---|
| 0000 | 0 | 0 |
| 0111 | 7 | 7 |
| 1000 | 8 | −8 |
| 1101 | 13 | −3 |
| 1111 | 15 | −1 |
正の整数xを負にした−xの並びは、nビットのxの0と1をすべて反転し、1を加えて下位nビットを取ることで作れます。これは符号なしで見た値を2ⁿ−xにする操作です。反転だけでは1の補数であり、2の補数ではありません。xと2ⁿ−xの和は2ⁿとなり、下位nビットがすべて0になるため、加算で打ち消し合います。
8ビットの2の補数表現で−18を作り、負の重みを使って検算しよう。
解答・解説
18は00010010。反転すると11101101、1を足すと **11101110**。−128+64+32+8+4+2=−18です。符号なしとしてなら238ですが、符号付きとして読むと238−256=−18になります。
4. 繰り上がりとオーバーフローを分ける
以下は「加算後に下位nビットだけを残す」という計算モデルです。符号なし加算では、最上位の外への繰り上がりがあると数学的な和が表現範囲を超えています。一方、2の補数の符号付き加算では、同符号の2数を足したのに結果の符号が変わったときにオーバーフローが起きます。異符号の2数の和は、両方が元の範囲内なら範囲を超えません。
4ビットで0111+0001=1000となった。符号なしの場合と2の補数の場合を別々に評価しよう。
解答・解説
符号なしでは7+1=8で、0〜15の範囲内なので正しい結果です。2の補数では7+1の数学的な和8が−8〜7の範囲外で、1000は−8と読まれます。**外への繰り上がりはなくても符号付きオーバーフローは起きます。**
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進) | バイト数 |
|---|---|---|---|
| A | U+0041 | 41 | 1 |
| 1 | U+0031 | 31 | 1 |
| あ | U+3042 | E3 81 82 | 3 |
数値の1を8ビットの符号なし整数として保存するなら00000001です。しかし文字「1」をUTF-8で保存するなら00110001(16進31、数値として読めば49)です。また、文字列「13」はUTF-8で31 33という2バイトであり、数値13の8ビット表現00001101とは違います。表示が似ていても、数値への変換と文字列の連結は別の操作です。
自作の短い文字列「Aあ1」をUTF-8で保存する。BOM、改行、終端文字などの付加情報は含めない。何バイト、何ビットになるか。
解答・解説
表からバイト列は41 E3 81 82 31で、1+3+1= **5バイト=40ビット**。この例の文字は3個ですが、文字数をそのままバイト数にはできません。「あ」が3バイトだからといって、すべての日本語の文字が常に3バイトになるとも限りません。
6. 練習:まず解釈とビット数を指定する
8ビット列10100110を、符号なし整数と2の補数の両方で読もう。
解答・解説
符号なしは128+32+4+2=166。2の補数は−128+32+4+2=−90です。同じ並びでも最上位の重みが異なります。
8ビットで−37を2の補数表現にせよ。
解答・解説
37は00100101。反転して11011010、1を足して11011011です。符号なしの値219から256を引くと−37で検算できます。
8ビットの2の補数で01100100と00110010を足す。数学的な和、残るビット列、その解釈を示し、オーバーフローを判定せよ。
解答・解説
100+50=150ですが、範囲は−128〜127です。残る10010110は−128+16+4+2=−106。同符号の正数同士から負になっており、符号付きオーバーフローです。符号なしなら150で範囲内です。
文字列「11」をUTF-8で保存する場合と、数値11を8ビット符号なし整数として保存する場合の違いを答えよ。付加情報は無視する。
解答・解説
文字列は16進31 31で2バイト(16ビット)。数値は00001011で1バイト(8ビット)です。文字を二つ並べることと、その表記を数値に変換することは異なります。
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でも検算しています。