メモリとバイト:エンディアン・型・アドレス

メモリには、アドレスごとに一つのバイトが格納されています。大きな数は、複数のバイトにまたがります。どのバイトをひとまとまりにして、どんな順序で、どの型の数として読むか。それを決めるのは読み出し命令です。下で読み方を選ぶと、使われるバイトが光り、それぞれに掛ける位の重みが表示されます。

インタラクティブシミュレーションを読み込んでいます...

レッスン

理論 — メモリとバイト:エンディアン・型・アドレス

バイトは8ビットからなり、0〜255の値を表します。メモリの各アドレスには一つのバイトが格納されます。それより大きな数には連続したアドレスを使い、32ビット整数なら4個必要です。こうしたバイトの並び順をエンディアンと呼びます。リトルエンディアンでは最も小さいアドレスに最下位バイトを置き、ビッグエンディアンでは最上位バイトを置きます。四つのバイトを符号なし整数として読むと、値はb₀·256⁰ + b₁·256¹ + b₂·256² + b₃·256³です。bₖは位kにあるバイトを表します。符号付き整数として読む場合、この合計が2³¹以上なら2³²を引きます。浮動小数点数として読む場合は、同じビット列をIEEE 754の規則で解釈します。

各記号の意味

bₖ
位kにあるバイト。kは0(最下位)〜3
256ᵏ
その位の重み:1、256、65,536、16,777,216
前提
符号付き整数は2の補数、浮動小数点数はIEEE 754の単精度として扱います。現在のデスクトップやスマートフォンのプロセッサも、この方式です。テキストはASCIIで、一文字を一バイトで表します。現在、大半のファイルで使われているUTF-8では、一文字に最大四バイトを使います。

バイト順を逆に読むと、1が16,777,216になる 🖖

1を逆順で読むを押してみましょう。x86プロセッサや、スマートフォンで使われる設定のARMは、32ビット整数の1を保存するとき、最も小さいアドレスに01を置き、その後に値がゼロのバイトを三つ並べます。これがリトルエンディアンで、最下位バイトから格納する方式です。ビッグエンディアンでは先頭を最上位バイトとして読むため、同じ01に256³の重みが付き、値は16,777,216になります。バイト列は変わっていません。32ビットで表せる4,294,967,296通りの値のうち、どちらの順序で読んでも同じ値になるのは65,536通りだけ。最初と最後のバイトが等しく、内側の二つも等しい場合です。−1を表すFF FF FF FFも、その一つです。ダニー・コーエンがこの二つの方式に名前を付けたのは1980年。『ガリヴァー旅行記』に登場する、ゆで卵のどちらの端を割るかをめぐる争いにちなんでいます。

πも1,078,530,011も、バイト列は同じ 🖖

πを整数として読むを押してみましょう。πを32ビット浮動小数点数で保存すると、最も近い値である3.14159274に丸められ、DB 0F 49 40という四つのバイトになります。これを符号付き整数として読むと1,078,530,011、バイト順を逆にすると−619,755,200です。テキストとして読めば、表示できる文字はIと@の二つで、残りの二つのバイトは表示できません。どのバイトにも「浮動小数点数」とは書かれていないのです。リトルマンコンピュータでは、命令と数を区別できませんでした。バイトを格納するメモリも、数の型を区別できません。型を指定するのは読み出し命令なので、プログラムが誤った型で読んでも、エラーが出ないまま誤った値を得ることがあります。

アドレスが一つずれると、二つの正の数から負の数が現れる 🖖

アドレスを一つずらすを押してみましょう。アドレス0に1,000、アドレス4に2,000があり、どちらもリトルエンディアンです。バイト列はE8 03 00 00 D0 07 00 00。32ビットの読み出しをアドレス0ではなく1から始めると、最初の数から03 00 00、次の数からD0を取り出します。D0が最上位の位置に入るので、3 + 208 × 256³ = 3,489,660,931です。符号付き整数として読むと、D0の最上位ビットが立っているため、−805,306,365になります。結果には、保存したどちらの数も現れません。メモリには、数の終わりを示す目印がなかったのです。

全プロセスの詳細解説

  1. この画像の大きさは? 4 ステップ

    PNGヘッダーを押してみましょう。表示されるのはPNGファイルの先頭24バイトです。PNGの仕様では、一バイトを超える整数はすべてビッグエンディアンと定められています。また、IHDRチャンクのデータは幅、高さの順に始まり、それぞれに四バイトを使います。幅と高さを求め、プログラムがリトルエンディアンで読んだ場合にはどんな大きさになるかも計算してみましょう。

    この問題をツールで開く

    1. ファイルの先頭には、89 50 4E 47 0D 0A 1A 0Aという八バイトの識別用の並びがあります。続く四バイトは最初のチャンクの長さをビッグエンディアンで表したもので、00 00 00 0Dは13です。その次がチャンクの種類を示す49 48 44 52で、文字として読むとIHDR。したがって、チャンクのデータはアドレス16から始まります。

    2. 幅はアドレス16〜19の00 00 01 E0です。ビッグエンディアンでは01が256¹の位、E0、つまり224が256⁰の位に入るので、256 + 224 = 480。このツールでアドレス16から読んでも、同じ値が表示されます。

    3. 高さはアドレス20〜23の00 00 01 0Eです。256 + 14 = 270になります。

    4. リトルエンディアンでは、最後のバイトが最上位になります。幅は224 × 256³ + 1 × 256² = 3,758,161,920、高さは14 × 256³ + 256² = 234,946,560です。

    解答

    480 × 270ピクセルです。同じバイト列をリトルエンディアンで読むと、3,758,161,920 × 234,946,560、約8.83 × 10¹⁷ピクセルという大きさになります。一ピクセルに四バイトを使うなら、3.53 × 10¹⁸バイトです。バイト順が書かれているのはファイルではなく仕様書なので、プログラムにはその順序を指定する必要があります。

学習の道すじ

コンピュータはどうやってプログラムを動かすのか

参考文献 (2)