リトルエンディアンの概要
リトルエンディアンは、データをメモリに格納する際のバイト順序の一種です。
※バイト順序とは、複数バイトで構成されるデータをどのようにメモリに配置するかの方法を指します。データには例えば、32ビット(4バイト)の整数や64ビット(8バイト)の浮動小数点数などがありわけですが、それらのデータをどのような順番で並べていくか、という事についての決まり事です。
整数の例
例えば、32ビットの整数42をリトルエンディアン形式でメモリに格納する場合、42を16進数で表すと0x2Aになります。
※16進数についての詳細は↓の記事をご参照ください。
これを4バイトのリトルエンディアン形式で格納すると、以下のようになります。
まず、42の16進数表現である0x2Aをバイトごとに分割します。
整数42は32ビット(4バイト)のデータ型ですので、0x2A 0x00 0x00 0x00となります。
リトルエンディアン形式では、最下位バイトの0x2Aがメモリの先頭に配置され、その後に0x00が続きます。したがって、メモリには次のように格納されます:0x2A 0x00 0x00 0x00。
浮動小数点数の例
リトルエンディアン形式を理解するためのもう一つの例として、64ビットの浮動小数点数3.14159を考えてみます。
この値をリトルエンディアン形式でメモリに格納する場合、3.14159の64ビット表現は次のようになります:0x1F 0x85 0xEB 0x51 0xB8 0x1E 0x09 0x40。
この場合も、最下位バイト0x1Fがメモリの先頭に配置され、次に0x85が続き、最上位バイト0x40が最後に配置されます。
浮動小数点数のメモリ表現(3.14159の例)
浮動小数点数3.14159が64ビット(double型)のメモリ表現として0x1F(31) 0x85(133) 0xEB(235) 0x51(81) 0xB8(184) 0x1E(30) 0x09(9) 0x40(64)になる理由を詳しく説明します。
※16進数になじみのない方向けに()で10進数表記も併記しました。
IEEE 754標準
double型の浮動小数点数は、IEEE 754標準に従って次のように64ビットで表現されます。
3.14159のIEEE 754表現
- 符号ビット(数値の正負を表すビット): 3.14159は正の数なので、符号ビットは0です。
※負の数の場合は、符号ビットは1になります。 - 指数部(数値の大きさを表すビット): 3.14159を2進数に変換し、正規化(プログラムが取り扱うルールに則って値を整える作業)します。
3.14159の2進数表現はおおよそ次のようになります
11.001001000011111101111100111011
正規化すると:
1.1001001000011111101111100111011 × 2^1
この指数は1なので、バイアス1023(2の10乗 – 1)を加えます。
1 + 1023 = 1024
1024の2進数表現は
10000000000
仮数部(または有効数字部)(数値の精度を表すビット): 仮数部は正規化された後の小数部分です:
1001001000011111101111100111011
これを64ビットのIEEE 754標準に基づいて配置すると次のようになります:
符号ビット(数値の正負を表すビット) | 指数部(数値の大きさを表すビット)(11ビット) | 仮数部(または有効数字部)(数値の精度を表すビット)(52ビット)
0 | 10000000000(1024) | 1001001000011111101111100111011(残りは0で埋める)
16進数表現への変換
これを16進数に変換します。
- 符号ビット(数値の正負を表すビット): 0
- 指数部(数値の大きさを表すビット): 10000000000 = 0x400(16進数) = 1024(10進数)
- 仮数部(または有効数字部)(数値の精度を表すビット): 1001001000011111101111100111011 = 0x921FB54442D18(16進数) = 1019660462256671288(10進数)
0100 0000 0000 1001 0010 0001 1111 1011 1101 1111 0011 1011 0000 0000 0000 0000
リトルエンディアン形式への変換
リトルエンディアン形式では、バイトオーダーが逆になるため次のようになります:
※()内は対応する10進数表記です。
18(24) 2D(45) 44(68) 54(84) FB(251) 21(33) 09(9) 40(64)
よって、3.14159の64ビットにおけるリトルエンディアン形式の表現は:
0x18(24) 0x2D(45) 0x44(68) 0x54(84) 0xFB(251) 0x21(33) 0x09(9) 0x40(64)
となります。
char型について
char型は、1バイト(8ビット)のデータ型で、整数としても文字としても扱うことができます。
char型のリトルエンディアン表現について考えると、実際にはchar型のサイズが1バイトであるため、リトルエンディアンやビッグエンディアンの違いが存在しません。
なぜなら、1バイトはそれ自体で完結しており、バイト順序を気にする必要がないからです。
例えば、文字’M’のASCIIコードは77(10進数)で、16進数表記では0x4Dです。これをメモリに格納すると、次のようになります。
char c = 'M';
とする場合、メモリにはそのまま0x4D(77)が格納されます。
例えば、文字’M’のASCIIコードは77(10進数)で、16進数表記では0x4Dです。これをメモリに格納すると、次のようになります。
char c = 'M';
とする場合、メモリにはそのまま0x4D(77)が格納されます。
char型の例
以下は、char型の変数に文字を格納し、そのメモリ表現を確認する例です。
char c = 'M';
Print((int)c); // 77が出力される
このコードでは、文字’M’をchar型の変数に代入しています。
これは、実際には’M’のASCIIコードである77が変数c
に格納されることを意味します。
char型を用いることで、文字とそのASCIIコードを相互に扱うことができます。
short型について
Short型は16ビット(2バイト)の整数型です。リトルエンディアン形式では、最下位バイトが先頭に格納され、最上位バイトが最後に格納されます。
例えば、16ビット整数258(0x0102)をリトルエンディアン形式でメモリに格納する場合:
short型の例
以下は、Short型の変数に整数を格納し、そのメモリ表現を確認する例です。
short s = 258;
// メモリ表現:0x02 0x01
Print("s[0] = ", (int)((uchar*)&s)[0]); // 2が出力される
Print("s[1] = ", (int)((uchar*)&s)[1]); // 1が出力される
このコードでは、整数258をShort型の変数に代入しています。
リトルエンディアン形式では、最下位バイト0x02が先頭に、最上位バイト0x01が最後に格納されます。
long型について
long型は32ビット(4バイト)の整数型です。リトルエンディアン形式では、最下位バイトが先頭に格納され、最上位バイトが最後に格納されます。
例えば、32ビット整数305419896(0x12345678)をリトルエンディアン形式でメモリに格納する場合:
long型の例
以下は、long型の変数に整数を格納し、そのメモリ表現を確認する例です。
long l = 305419896;
// メモリ表現:0x78 0x56 0x34 0x12
Print("l[0] = ", (int)((uchar*)&l)[0]); // 120が出力される -> 0x78
Print("l[1] = ", (int)((uchar*)&l)[1]); // 86が出力される -> 0x56
Print("l[2] = ", (int)((uchar*)&l)[2]); // 52が出力される -> 0x34
Print("l[3] = ", (int)((uchar*)&l)[3]); // 18が出力される -> 0x12
long l = 305419896;
は、16進数で表すと0x12345678
となります。- リトルエンディアン形式では、メモリに
0x78 0x56 0x34 0x12
と格納されます。 Print("l[0] = ", (int)((uchar*)&l)[0]);
は、最下位バイト0x78
(16進数)を表示します。10進数では120
です。Print("l[1] = ", (int)((uchar*)&l)[1]);
は、次のバイト0x56
(16進数)を表示します。10進数では86
です。Print("l[2] = ", (int)((uchar*)&l)[2]);
は、次のバイト0x34
(16進数)を表示します。10進数では52
です。Print("l[3] = ", (int)((uchar*)&l)[3]);
は、最上位バイト0x12
(16進数)を表示します。10進数では18
です。
float型について
float
型は32ビット(4バイト)の浮動小数点数型で、IEEE 754標準に従ってメモリに格納されます。リトルエンディアン形式では、バイトオーダーが逆になります。
例えば、値が1.0の場合、IEEE 754形式に基づいてメモリに格納されます。
1.0のIEEE 754表現は次のようになります:
- 符号ビット: 0
- 指数部: 127(バイアスを考慮)
- 仮数部: 0
これを16進数に変換すると:
- 0x3F800000(ビッグエンディアン形式)
リトルエンディアン形式では、0x00 0x00 0x80 0x3Fと格納されます。
float型の例
以下は、float
型の変数に値を格納し、そのメモリ表現を確認する例です。
float f = 1.0;
// メモリ表現:0x00 0x00 0x80 0x3F
Print("f[0] = ", (int)((uchar*)&f)[0]); // 0が出力される -> 0x00
Print("f[1] = ", (int)((uchar*)&f)[1]); // 0が出力される -> 0x00
Print("f[2] = ", (int)((uchar*)&f)[2]); // 128が出力される -> 0x80
Print("f[3] = ", (int)((uchar*)&f)[3]); // 63が出力される -> 0x3F
このコードでは、値1.0をfloat
型の変数に代入しています。リトルエンディアン形式では、最下位バイト0x00が先頭に、最上位バイト0x3Fが最後に格納されます。
このように、各データ型についてリトルエンディアン形式でのメモリ表現を確認することができます。