エンディアン

エンディアン（endianness）あるいはバイトオーダ（byte order）は、コンピュータの記憶装置に複数バイトからなる数値を配置する規則。 記憶装置は通信路ともみなせるため、通信で複数バイトを扱う際に、送る順序の規則でもある。 コンピュータで扱う数値は、1バイトや2バイト、4バイトや8バイトなど複数の処理単位がある。 そのため記憶装置を汎用化し最小の1バイト単位でも扱えるように、1バイト毎に番地(アドレス)が連続して割り振られている。 複数バイトの数値を複数アドレスの記憶装置に配置する規則であるバイトオーダも複数ある。 特に、 数値の1番小さい桁1バイト分を、1番大きいアドレスの記憶装置に配置し順に並べる規則はビッグエンディアンと呼ぶ。 対称的に、 数値の1番小さい桁1バイト分を、1番小さいアドレスの記憶装置に配置し順に並べる規則をリトルエンディアンと呼ぶ。 バイトオーダの種類は、2バイト数値では2!=2*1=2種類、4バイト数値では4!種類、8バイト数値では8!種類もあるが、不規則な並びを採用する必然性が無く、いずれもリトルエンディアンかビッグエンディアンの2種類に収束している。 1バイト未満のビット単位の並び順序であるビットオーダは、レジスタ内順序や信号線の接続順序として存在するが、命令セットやユーザープログラムの観点からは不要で隠蔽されている。 4バイトの数値0x01234567を記憶するために、4バイトの連続した記憶装置に並べる例を提示する。 16進数表記した上記数値の一番小さい桁の1バイト分は0x67であり、一番大きい桁の1バイト分は0x01である。 表のアドレスは、左が小さいアドレス80で、右が大きいアドレス83を示すことに注意。 下記は、記憶装置でのバイトオーダを表示し、規則を簡易的に判定する例である。 表と同じく左が小さいアドレスで、右が大きいアドレスで表示している。 #include <stdint.h> #include <stdio.h> int isLittleEndian(void) { int i = 1; uint8_t *p = (uint8_t *) &i; return *p; } int main(int argc, char *argv[]) { uint64_t i8 = 0x0123456789abcdef; uint32_t i4 = 0x01234567; uint16_t i2 = 0x0123; double d = -1.0/3.0; uint8_t *p; /* 8ByteOrder */ p = (uint8_t *) &i8; printf("8Byte Order %016lx\n", i8); printf(" on Memory |%02x|%02x|%02x|%02x|%02x|%02x|%02x|%02x|\n", *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7)); /* 4ByteOrder */ p = (uint8_t *) &i4; printf("4Byte Order %08x\n", i4); printf(" on Memory |%02x|%02x|%02x|%02x|\n", *p, *(p+1), *(p+2), *(p+3)); /* 2ByteOrder */ p = (uint8_t *) &i2; printf("2Byte Order %04x\n", i2); printf(" on Memory |%02x|%02x|\n", *p, *(p+1)); /* 8Byte Double Order */ p = (uint8_t *) &d; printf("DoubleOrder %lf == %le\n", d, d); printf(" on Memory |%02x|%02x|%02x|%02x|%02x|%02x|%02x|%02x|\n", *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7)); /* UTF8 strings */ p = (uint8_t *) "01🥺語\n"; printf("StringOrder %s", p); printf(" on Memory |%02x|%02x|%02x|%02x|%02x|%02x|%02x|%02x|%02x|%02x|%02x|\n", *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), *(p+8), *(p+9), *(p+10)); if (isLittleEndian()) { printf("is LittleEndian\n"); } else { printf("is Not LittleEndian\n"); } return 0; } リトルエンディアン、日本語UTF-8のLinux環境での処理結果 8Byte Order 0123456789abcdef on Memory |ef|cd|ab|89|67|45|23|01| 4Byte Order 01234567 on Memory |67|45|23|01| 2Byte Order 0123 on Memory |23|01| DoubleOrder -0.333333 == -3.333333e-01 on Memory |55|55|55|55|55|55|d5|bf| StringOrder 01🥺語 on Memory |30|31|f0|9f|a5|ba|e8|aa|9e|0a|00| is LittleEndian IBMのメインフレーム（および互換機）、モトローラのMC68000（および後継）、サン・マイクロシステムズのSPARCなどはビッグエンディアンを採用し、DECのVAX、インテルのx86などはリトルエンディアンを採用している。ARMアーキテクチャ、PowerPCなど、エンディアンを切り替えられるバイエンディアン (bi-endian) のプロセッサも存在する。 言語処理系などの仮想マシンの類では、プラットフォームに応じ使い分ける設計のものもあれば、片方に寄せる設計のものもある。例えば、Java仮想マシンはプラットフォームを問わずビッグエンディアンである。 エンディアンの相違は、単一あるいは同種のシステムに閉じた運用をする限りでは通常は問題にならない。それ以上のことをする場合の、問題が起こりやすい例を示す。 ネットワークを通してバイト単位でデータをやりとりする場合 異なるシステム間でバイナリファイルなどを交換する場合 異なるシステムにプログラムを移植する場合 構成するプロセッサが異なるマルチプロセッサ環境で共有メモリを使用する場合 TCP/IPプロトコルスタックでは、ビッグエンディアンに統一しており、それをネットワークバイトオーダという。この分野では、それに対し、各コンピュータのエンディアンをホストバイトオーダという。 画像や音声などのバイナリファイルにおいても、異なるコンピュータ間の互換性を確保するため、通常はエンディアンが規定される。 Unicodeにおいても、構成要素が多バイトとなるエンコーディング（主にUTF-16）では、エンディアンが問題となる。そのため、バイト順マーク（英: Byte Order Mark、略語：BOM）と呼ばれる特殊なコード (U+FEFF) が予約されており、データの先頭にこれを付与することで、データを受け取る側がエンディアンを判別できるようになっている。BOMがない場合には、ビッグエンディアンだと決められている（→ UTF-16）。 ただし、復号側が以上のルールでエンディアンを判別する狭義のUTF-16とは別に、エンディアンを事前に一方に決定しているUTF-16BEとUTF-16LEが存在する。Windows上の文書における「Unicodeテキスト」は、BOMがない場合、UTF-16LE（リトルエンディアン）である。 エンディアンは、日付の年月日の表現の分類にも使われる。2021年4月12日を例に取ると、 年月日の順（2021/04/12）　ビッグエンディアン Big-Endian　（中国、日本、韓国など） 日月年の順（12/04/2021）　リトルエンディアン Little-Endian　（英国、フランス、ドイツ、イタリア、ロシアなど） 月日年の順（04/12/2021）　ミドルエンディアン Middle-Endian　（米国） 日付の国際規格であるISO 8601では、ビッグエンディアンのみが認められている。ただし、区切りの符号は、「/」ではなく、「-」でなければならない（例：2021-04-12）。 ビッグエンディアンとリトルエンディアンという語は、ジョナサン・スウィフトの風刺小説『ガリヴァー旅行記』の中のエピソードに由来する。ガリヴァー旅行記の第1部「小人国」では、ゆで卵を丸い方（大きい方）の端 (big end) から割る人々（英: Big-Endians）と尖った方（小さい方）の端 (little end) から割る人々 (英: Little-Endians) との対立が描かれている。 この語を計算機に転用したのはダニー・コーエンで、1980年4月1日に発表したジョークRFC"On Holy Wars and a Plea for Peace"（聖戦と平和の嘆願について）で初めて使用した。