Intelプロセッサ(およびおそらく他のいくつか)は、ストレージにリトルエンディアン形式を使用します。
なぜ誰かがバイトを逆の順序で格納したいのかといつも思っています。この形式には、ビッグエンディアン形式に比べて利点がありますか?
どちらの方法にも引数がありますが、1つのポイントは、リトルエンディアンシステムでは、メモリ内の指定された値のアドレスが32、16、または8ビット幅として同じであることです。
つまり、メモリに2バイトの値がある場合:
0x00f0 16
0x00f1 0
その '16'を16ビット値(ほとんどの32ビットシステムではc 'short')または8ビット値(一般にc 'char')として取ると、使用するフェッチ命令のみが変更され、フェッチするアドレスは変更されませんから。
ビッグエンディアンシステムでは、上記は次のようにレイアウトされます。
0x00f0 0
0x00f1 16
ポインタをインクリメントしてから、新しい値に対してより狭いフェッチ操作を実行する必要があります。
つまり、「リトルエンディアンシステムでは、キャストはノーオペレーション」です。
なぜ誰かがバイトを逆の順序で格納したいのかといつも思っています。
ビッグエンディアンとリトルエンディアンは、人間の観点からは「通常の順序」と「逆の順序」のみであり、これらすべてが真である場合にのみ...
これらはすべて、CPUにはまったく関係のない人間の慣例です。 #1と#2を保持し、#3を反転させると、右から左に書かれるアラビア語またはヘブライ語を読む人にとって、リトルエンディアンは「完全に自然」に見えるでしょう。
そして、ビッグエンディアンを不自然に見えるようにする他の人間の慣習があります...
私が主に68KとPowerPCをプログラミングしていた頃は、ビッグエンディアンは「正しい」と考え、リトルエンディアンは「間違っている」と考えていました。しかし、私はより多くのARM=およびIntelの作業を行ってきたので、リトルエンディアンに慣れてきました。それは本当に問題ではありません。
わかりました、これは私が説明してきた理由です:加算と減算
マルチバイトの数値を加算または減算する場合は、最下位バイトから開始する必要があります。たとえば、16ビットの数値を2つ追加する場合、最下位バイトから最上位バイトへの桁上げが発生する可能性があるため、最下位バイトから始めて桁上げの有無を確認する必要があります。これは、ロングハンド加算を行うときに右端の数字から始めるのと同じ理由です。左から始めることはできません。
メモリからバイトを順次フェッチする8ビットシステムを考えてみます。最下位バイトfirstをフェッチする場合、加算while最上位バイトがメモリからフェッチされています。この並列処理が、システムなどのリトルエンディアンでパフォーマンスが向上する理由です。両方のバイトがメモリからフェッチされるまで待つか、逆の順序でフェッチする必要がある場合は、さらに時間がかかります。
これは古い8ビットシステムです。最近のCPUでは、バイトオーダーに違いがあるとは思えないので、歴史的な理由でのみリトルエンディアンを使用します。
8ビットプロセッサを使用すると、より効率的であり、別のコードを使用したり、追加の値をバッファしたりすることなく、8ビットまたは16ビットの操作を実行できます。
一度に1バイトずつ処理する場合は、いくつかの追加操作の方が適しています。
ただし、ビッグエンディアンがより自然である理由はありません。英語では、13(リトルエンディアン)と23(ビッグエンディアン)を使用します。
日本の日付規則は「ビッグエンディアン」です-yyyy/mm/dd。これは、通常の最初の文字が最も重要なルールと単純な文字列比較を使用できるソートアルゴリズムに便利です。
同様のことが、最上位のフィールドが最初のレコードに格納されているビッグエンディアン番号にも当てはまります。フィールド内のバイトの重要度の順序は、レコード内のフィールドの重要度と一致するため、memcmp
を使用してレコードを比較できます。2つのロングワード、4つのワード、8つを比較するかどうかはあまり気になりません。別々のバイト。
フィールドの重要度の順序を逆にすると、同じ利点が得られますが、ビッグエンディアンではなくリトルエンディアンの数値に対してです。
もちろん、これには実際的な意味はほとんどありません。プラットフォームがビッグエンディアンでもリトルエンディアンでも、本当に必要な場合は、レコードフィールドを注文してこのトリックを利用できます。 portableコードを記述する必要がある場合、それはただの苦痛です。
クラシックな魅力へのリンクを含めることもできます...
http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt
[〜#〜]編集[〜#〜]
余分な考え。私はかつて大きな整数ライブラリーを書いた(できるかどうかを確認する)ために、32ビット幅のチャンクは、プラットフォームがそれらのチャンク内のビットをどのように配列するかに関係なく、リトルエンディアン順で格納されます。理由は...
多くのアルゴリズムは、当然ながら最下位の端で動作し始め、それらの端が一致することを望みます。さらに、たとえば、桁上げはますます有効桁に伝播するため、最下位から開始することが理にかなっています。
値の増減は、最後にチャンクを追加/削除することを意味します。チャンクを上下にシフトする必要はありません。メモリの再割り当てが原因で、コピーが必要になる場合もありますが、多くの場合は必要ありません。
もちろん、これはプロセッサとの明確な関連性はありません-CPUがハードウェアのビッグ整数サポートで作成されるまで、それは純粋にライブラリのことです。
他の誰も、なぜこれが行われるのか、結果について多くのことを答えていません。
所定のクロックサイクルでメモリから1バイトをロードできる8ビットプロセッサを考えてみます。
ここで、16ビット値を(たとえば)所有している唯一の16ビットレジスタ(プログラムカウンターなど)にロードする場合、簡単な方法は次のとおりです。
結果:フェッチ位置をインクリメントするだけで、より広いレジスターの下位部分にロードするだけで、左シフトできるだけで済みます。 (もちろん、右にシフトすることは他の操作にも役立ちますので、これは少しサイドショーです。)
この結果、16ビット(2バイト)のものが、Most..Leastの順に格納されます。つまり、アドレスが小さいほど最上位のバイトになり、ビッグエンディアンになります。
代わりにリトルエンディアンを使用してロードしようとした場合、ワイドレジスタの下部にバイトをロードしてから、次のバイトをステージング領域にロードし、シフトして、より広いレジスタの上部にポップする必要があります。 。または、ゲートのより複雑な配置を使用して、上位バイトまたは下位バイトに選択的にロードできるようにします。
リトルエンディアンにしようとすると、より多くのシリコン(スイッチとゲート)またはより多くの操作が必要になります。
言い換えれば、昔は見返りに見返りが得られるという点で、ほとんどのパフォーマンスと最小のシリコン領域でより多くの効果があります。
最近では、これらの考慮事項とほとんど無関係ですが、パイプラインフィルのようなものmayは、まだ少し重要です。
S/wの記述に関しては、リトルエンディアンアドレス指定を使用する方が簡単な場合がよくあります。
(ビッグエンディアンプロセッサは、バイト順ではビッグエンディアンになり、バイト単位のビットではリトルエンディアンになる傾向があります。しかし、一部のプロセッサは奇妙で、ビッグエンディアンのビット順とバイト順を使用します。これにより、 veryメモリマップされたペリフェラルを追加するハードウェア設計者にとって興味深いですが、プログラマにとって他の影響はありません。)
ジムワイズは良い点を作りました。別の問題があり、リトルエンディアンでは次のことができます。
byte data[4];
int num=0;
for(i=0;i<4;i++)
num += data[i]<<i*8;
OR
num = *(int*)&data; //is interpreted as
mov dword data, num ;or something similar it has been some time
メモリ内のスワップされた場所の明らかな欠点の影響を受けないプログラマにとっては、より簡単です。私は個人的にビッグエンディアンが自然なものの逆であると思います:)。 12は21として保存および記述します。
なぜ誰かがバイトを逆順で保存したいのかといつも思っています
10進数はビッグエンディアンで表記されています。それはまたあなたがそれを英語でどのように書くかもあなたは最も重要な数字から始めてそして次に最も重要なものから最も重要でないものへと続く。例えば.
1234
千、二百三十四です。
これは、ビッグエンディアンが自然順序と呼ばれることもあります。
リトルエンディアンでは、この数は1、20、300、4000になります。
ただし、加算や減算などの演算を実行するときは、最後から始めます。
1234
+ 0567
====
4と7から始めて、最下位の桁を書き、キャリーを覚えます。次に、3と6などを追加します。メモリを順番に読み取るロジックがすでにある場合、数値を逆にすると、加算、減算、または比較の実装が簡単になります。
この方法でビッグエンディアンをサポートするには、メモリを逆に読み取るロジックが必要です。または、レジスタでのみ動作するRISCプロセスがあります。 ;)
Intel x86/AMD x64デザインの多くは歴史的なものです。
ビッグエンディアンは、いくつかの操作に役立ちます(等しいオクテット長の「bignums」の比較が思い浮かびます)。その他のリトルエンディアン(2つの「ビッグナム」を追加する可能性があります)。結局のところ、それはCPUハードウェアが何に設定されているかによって異なりますが、通常はどちらかです(一部のMIPSチップはIIRC、起動時にLEまたはBEに切り替え可能)。
可変長のストレージと転送のみが含まれ、複数の値を持つ算術演算が含まれない場合、LEは通常、書き込みが容易であり、BEは読み取りが容易です。
具体的な例として、intからstringへの変換(およびその逆)を考えてみましょう。
int val_int = 841;
char val_str[] = "841";
Intが文字列に変換されると、最下位桁が最上位桁よりも抽出しやすくなります。それはすべて、単純な終了条件を持つ単純なループで実行できます。
val_int = 841;
// Make sure that val_str is large enough.
i = 0;
do // Write at least one digit to care for val_int == 0
{
// Constants, can be optimized by compiler.
val_str[i] = '0' + val_int % 10;
val_int /= 10;
i++;
}
while (val_int != 0);
val_str[i] = '\0';
// val_str is now in LE "148"
// i is the length of the result without termination, can be used to reverse it
次に、BEの順序で同じことを試してください。通常、特定の数(ここでは100)に対して最大の10の累乗を保持する別の除数が必要です。もちろん、最初にこれを見つける必要があります。やるべきことはもっとたくさんあります。
文字列からintへの変換は、逆書き込み操作として行われる場合、BEで行う方が簡単です。書き込みでは、最上位の桁が最後に格納されるため、最初に読み取る必要があります。
val_int = 0;
length = strlen(val_str);
for (i = 0; i < length; i++)
{
// Again a simple constant that can be optimized.
val_int = 10*val_int + (val_str[i] - '0');
}
LEの順序で同じことを行います。この場合も、1から始まり、各桁に10を掛ける追加の係数が必要になります。
したがって、私は通常、ストレージにBEを使用することを好みます。値が正確に1回だけ書き込まれるが、少なくとも1回、場合によっては何度も読み取られるためです。より単純な構造のために、私は通常、LEに変換し、2回目に値を書き込んでも、結果を元に戻すルートに進みます。
BEストレージのもう1つの例は、UTF-8エンコーディングなどです。