コンピューターサイエンス入門:理論編
エンディアンの入れ替え (uint32)
この回でやること
整数を記録するバイト順を読み解き、4バイトの反転と読み違いを確かめます。
- 読む 約 7 分
- 最後にクイズ 1 問
別のマシンが書き出したファイルを読んだら、数字が桁違いに大きくなっていた。バイトは1つも壊れていないのに、値だけが違う。こういうことが起こります。原因は、バイトを並べる向きです。
同じバイトでも値が変わる
4バイトの整数をメモリやファイルに置くとき、上の桁のバイトから置く流儀がビッグエンディアン、下の桁から置く流儀がリトルエンディアンです。16進数の2桁が1バイトに対応します。
プレーンテキスト
整数 0x12345678 を4バイトで置く
ビッグエンディアン 12 34 56 78
リトルエンディアン 78 56 34 12読み手が向きを取り違えると、別の値になります。2バイトで見ると差が分かります。
Python
data = bytes([0x00, 0x50])
print(int.from_bytes(data, "big")) # 80
print(int.from_bytes(data, "little")) # 20480int.from_bytes はバイトの並びを、指定した順序で整数として読みます。16進数の50は10進数の80です。逆向きでは80が256倍の桁へ移り、20480になります。ポート番号80も、バイト順を誤れば違う接続先になります。
向きは仕様で決める
IPやTCPのヘッダにある複数バイトの整数は、ビッグエンディアンで扱います。これをネットワークバイトオーダーと呼びます。PCのCPUは多くがリトルエンディアンです。通信の仕様とは別の約束です。
Cの htonl や ntohl は、この変換の道具です。書き手と読み手でバイト順をそろえ、ファイル全体ではなく、それぞれの整数の幅を区切って扱います。
反転するのはバイトの順番
32ビットは4バイトです。16進数を2桁ずつ区切り、組の順序を逆にします。組の中身は変えません。
JavaScriptのビット演算は32ビットの符号付き整数として扱うので、上位ビットが1なら負の数に見える場合があります。
JavaScript
console.log(1 << 31); // -2147483648
console.log((1 << 31) >>> 0); // 2147483648>>> 0 は同じ32ビットを符号なしの数値として扱います。バイト順は変わりません。
例題で確かめる
符号なし32ビット整数 0x12345678 のバイト順を反転します。12 / 34 / 56 / 78 と区切り、逆順の 78 / 56 / 34 / 12 にします。結果は 0x78563412 です。16進数の1桁ずつを逆にした 0x87654321 とは違います。もう一度反転すると元の値へ戻ります。