エンディアン

出典: フリー百科事典『ウィキペディア(Wikipedia)』
リトルエンディアンから転送)

エンディアン: endianness)あるいはバイトオーダ(byte order)は、コンピュータ記憶装置に複数バイト(多バイト)からなる数値を記憶する際の、各バイトの順序についての規則 [1] [2]。また、通信で複数バイトを扱う際の、送る順序についての規則。[注釈 1] [3]

概要[編集]

コンピュータで扱う数値(やデータ)は、1バイトで表現されるもの以外に、2バイト、4バイト、8バイトなど複数バイト(多バイト)で表現されるものがある。 一方、記憶装置は汎用化され、最小の1バイト単位でも扱えるように、1バイト毎に番地(アドレス)が連続して割り振られている。 すると、多バイトの数値やデータの各バイトをどのような順序で記憶装置に格納するかについての規則は、いくつか種類(選択肢)があることになる。

主に使われるバイトオーダ[編集]

ビッグエンディアン(右)とリトルエンディアン(左)

まず主につかわれている種類を解説すると、 特に、数値の1番小さい桁1バイト分を、1番大きいアドレスの記憶装置に配置し順に並べる規則はビッグエンディアンという。 それと対称的に、数値の1番小さい桁1バイト分を、1番小さいアドレスの記憶装置に配置し順に並べる規則をリトルエンディアンという。 [1][2]

ビッグエンディアンを採用しているコンピュータやCPUとしては、IBMメインフレーム(および互換機)、モトローラMC68000(および後継)、サン・マイクロシステムズSPARCなどがある。

リトルエンディアンを採用しているものとしては、DECVAXインテルx86Appleシリコン[注釈 2]、などがある。

エンディアンを切り替えられるバイエンディアン (bi-endian) のプロセッサとしては、PowerPCなどがある。なおARMアーキテクチャのコアは両モードをサポートするが、デフォルトの設定としてはリトルエンディアンとしており、ARM向けLinuxディストリビューションのほとんどはリトルエンディアンのみにしようと試みている[4]

言語処理系などの仮想マシンの類では、プラットフォームに応じエンディアンを使い分ける設計のものもあれば、片方に寄せる設計のものもある。例えば、Java仮想マシンはプラットフォームを問わずビッグエンディアンである。

理論上の可能性[編集]

なおバイトオーダの種類の理論的な数(理論上の選択肢数)は、数学の順列の考え方で理解すればよく、階乗で計算でき[5]、2バイト数値では2!=2*1=2種類、4バイト数値では4!=24種類、8バイト数値では8!=40320種類もある。(理論上はこれだけあるが、ビッグエンディアンやリトルエンディアンが単純で使いやすく、他のややこしい順序をわざわざ選ぶ必然性は無いので、通常どちらかが使われている。)[注釈 3]

前節で説明した現実に使われている主要な2種に加えて、理論上は可能な他のバイトオーダの具体例の一部を示すため、4バイトの数値0x01234567を記憶するために、4バイトの連続した記憶装置に並べる例を下で提示する。 16進数表記した上記数値の一番小さい桁の1バイト分は0x67であり、一番大きい桁の1バイト分は0x01である。 表のアドレスは、左が小さいアドレス80で、右が大きいアドレス83を示すことに注意。

4バイト数値0x01234567の配置[1][2]
アドレス 80 81 82 83
ビッグエンディアン 01 23 45 67
リトルエンディアン 67 45 23 01
PDP-11 23 01 67 45
非現実的な規則の一例 45 67 23 01

判定プログラムの例[編集]

下記は、記憶装置でのバイトオーダを表示し規則を簡易的に判定するプログラムの例である。C言語で書かれており、 表と同じく左が小さいアドレスで、右が大きいアドレスで表示している。

#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


互換性・移植性[編集]

単一あるいは同種のコンピュータで閉じたシステムならば、通常は内部のエンディアンの相違も含めてあらかじめ調整や対策が施してあるので、問題となることはほぼ無い。一方、次のような場合ではエンディアンの相違が問題となり正常な動作をしないことがある。

TCP/IPプロトコルスタックでは、ビッグエンディアンに統一しており、それをネットワークバイトオーダという。この分野では、それに対し、各コンピュータのエンディアンをホストバイトオーダという。

画像や音声などのバイナリファイルにおいても、異なるコンピュータ間の互換性を確保するため、通常はエンディアンが規定される。

Unicodeにおいても、構成要素が多バイトとなるエンコーディング(主にUTF-16)では、エンディアンが問題となる。そのため、バイト順マーク[6]: Byte Order Mark、略語:BOM)と呼ばれる特殊なコード (U+FEFF) が予約されており、データの先頭にこれを付与することで、データを受け取る側がエンディアンを判別できるようになっている。BOMがない場合には、ビッグエンディアンだと決められている(→ UTF-16)。

ただし、復号側が以上のルールでエンディアンを判別する狭義のUTF-16とは別に、エンディアンを事前に一方に決定しているUTF-16BEとUTF-16LEが存在する。Windows上の文書における「Unicodeテキスト」は、BOMがない場合、UTF-16LE(リトルエンディアン)である。


日付表現のエンディアン[編集]

エンディアンは、日付の年月日の表現の分類にも使われる[7]。2021年4月12日を例に取ると、

  • 年月日の順(2021/04/12) ビッグエンディアン Big-Endian (中国日本、韓国など)
  • 日月年の順(12/04/2021) リトルエンディアン Little-Endian (英国フランスドイツイタリアロシアなど)
  • 月日年の順(04/12/2021) ミドルエンディアン Middle-Endian (米国

日付の国際規格であるISO 8601では、ビッグエンディアンのみが認められている。ただし、区切りの符号は、「/」ではなく、「-」でなければならない(例:2021-04-12)。

歴史[編集]

「エンディアン」の語源[編集]

ビッグエンディアンリトルエンディアンという語は、18世紀にジョナサン・スウィフトが書いた風刺小説『ガリヴァー旅行記』の中のエピソードに由来する。ガリヴァー旅行記の第1部「小人国」では、ゆで卵を丸い方(大きい方)の端 (big end) から割る人々(: Big-Endians)と尖った方(小さい方)の端 (little end) から割る人々 (: Little-Endians) の党派的な対立が描かれている。

この語を計算機に転用したのはダニー・コーエンで、1980年4月1日に発表したジョークRFC "On Holy Wars and a Plea for Peace"[8][9](聖戦と平和の嘆願について)で初めて使用した。

脚注[編集]

  1. ^ a b c ブライアン・カーニハンロブ・パイク『プログラミング作法』アスキー出版ISBN 4-7561-3649-4 
  2. ^ a b c 中森章『マイクロプロセッサ・アーキテクチャ入門 インターフェース増刊 TECHI Vol.20』CQ出版 
  3. ^ 村松純、岩田賢一、有村光晴、渋谷智治『情報理論』オーム社ISBN 978-4-274-20595-8 
  4. ^ [1]
  5. ^ [2]
  6. ^ Unicode Terminology English - Japanese, B, Unicode, Inc.
  7. ^ Date Format Variations: Little-Endian, Middle-Endian, Big-Endian proofreading academy
  8. ^ IEN 137 (1 April 1980) http://www.ietf.org/rfc/ien/ien137.txt これ以前の用語が「byte order」であったことなどもわかる
  9. ^ D. Cohen. 1981. On Holy Wars and a Plea for Peace. Computer 14, 10 (October 1981), 48-54. doi:10.1109/C-M.1981.220208
  1. ^ 記憶装置は通信路ともみなせるため
  2. ^ 現在のMacはx86ベースのものとAppleシリコンベースのものがあるが、どちらもリトルエンディアンなので、どちらにしてもリトルエンディアンである。
  3. ^ なお余談だが、1バイト未満のビット単位の並び順序であるビットオーダは、レジスタ内順序や信号線の接続順序として電子回路的には存在しているが、プログラマにとっては不要な情報であり、命令セットアーキテクチャなどでは書かれず、隠蔽されている。

関連項目[編集]

外部リンク[編集]