Intelプロセッサー(およびおそらく他のプロセッサー)は、ストレージにリトルエンディアン形式を使用します。
なぜ誰かがバイトを逆順で保存したいのかといつも疑問に思います。この形式には、ビッグエンディアン形式よりも利点がありますか?
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の仕事を増やして以来、リトルエンディアンに慣れてきました。本当に関係ありません。
OK、説明した理由は次のとおりです。加算と減算
マルチバイト数を加算または減算する場合、最下位バイトから開始する必要があります。たとえば、2つの16ビット数を追加する場合、最下位バイトから最上位バイトへのキャリーが発生する可能性があるため、キャリーがあるかどうかを確認するには最下位バイトから開始する必要があります。これは、ロングハンド加算を行うときに右端の数字から開始するのと同じ理由です。左から始めることはできません。
メモリから連続してバイトをフェッチする8ビットシステムを検討します。最初に最下位バイトをフェッチする場合、最上位バイトがメモリからフェッチされている間に加算を開始できます。この並列性が、システムなどのリトルエンディアンでパフォーマンスが優れている理由です。両方のバイトがメモリからフェッチされるまで待機するか、逆の順序でフェッチする必要がある場合、時間がかかります。
これは古い8ビットシステムです。最近のCPUでは、バイトオーダーが違いを生むのではないかと疑っています。リトルエンディアンは歴史的な理由でのみ使用しています。
8ビットプロセッサの方が確かに効率的でした。別のコードや追加の値をバッファリングする必要なく、8ビットまたは16ビットの操作を実行できました。
一度に1バイトを処理している場合、いくつかの追加操作にとってはなお良いです。
しかし、ビッグエンディアンがより自然である理由はありません-英語では13(リトルエンディアン)と23(ビッグエンディアン)を使用します
0x12345678
は78 56 34 12
BEシステムの場合と同じように保存されます12 34 56 78
(バイト0は左側に、バイト3は右側にあります)。数が大きいほど(ビット単位で)、より多くのスワップが必要になることに注意してください。WORDには1つのスワップが必要です。DWORD、2パス(合計3回のスワップ); QWORD 3パス(合計7)など。つまり、(bits/8)-1
スワップ。別のオプションは、それらを前方と後方の両方で読み取ることです(各バイトを前方に読み取りますが、#全体を後方にスキャンします)。
日本の日付規則は「ビッグエンディアン」-yyyy / mm / ddです。これは、通常の最初の文字が最も重要なルールと単純な文字列比較を使用できるソートアルゴリズムに便利です。
同様のことが、最重要フィールド優先レコードに格納されているビッグエンディアンの数値にも当てはまります。フィールド内のバイトの重要度の順序は、レコード内のフィールドの重要度と一致するため、a memcmp
を使用して、2つのロングワード、4ワード、または8つの別個のバイトを比較するかどうかに関係なく、レコードを比較できます。
フィールドの重要度の順序を反転すると、同じ利点が得られますが、ビッグエンディアンではなくリトルエンディアンの数値に対してです。
もちろん、これには実用的な意味はほとんどありません。プラットフォームがビッグエンディアンであっても、リトルエンディアンであっても、本当に必要な場合は、レコードフィールドを注文してこのトリックを活用できます。移植可能なコードを書く必要がある場合、それはただの痛みです。
私も古典的な魅力へのリンクを含めることができます...
http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt
編集
余分な考え。私はかつて(可能かどうかを確認するために)大きな整数ライブラリを作成しました。そのため、プラットフォームがそれらのチャンク内のビットを順序付ける方法に関係なく、32ビット幅のチャンクはリトルエンディアン順に格納されます。その理由は...
多くのアルゴリズムは、最も重要ではない終わりから自然に機能し始め、それらの終わりが一致することを望んでいます。たとえば、キャリーはより多くの有効数字に伝播するため、最下位から開始することは理にかなっています。
値を大きくしたり小さくしたりすることは、最後にチャンクを追加/削除することを意味します-チャンクを上下に移動する必要はありません。メモリの再割り当てのためにコピーが必要な場合がありますが、頻繁ではありません。
もちろん、これはプロセッサとは明らかに関係ありません-CPUがハードウェアのビッグ整数サポートで作成されるまで、それは純粋にライブラリのものです。
なぜこれが行われるのか、結果に関する多くのことを誰も答えていない。
特定のクロックサイクルでメモリから1バイトをロードできる8ビットプロセッサを考えます。
さて、もしあなたが持っている唯一の16ビットレジスターに(例えば)16ビットの値をロードしたいのであれば-つまりプログラムカウンター、それをする簡単な方法は:
結果:フェッチ位置をインクリメントするだけで、より広いレジスタの下位部分にロードするだけで、左にシフトできる必要があるだけです。(もちろん、右へのシフトは他の操作に役立つため、これはちょっとしたサイドショーです。)
この結果、16ビット(ダブルバイト)のものは、Most..Leastの順に格納されます。つまり、小さいアドレスには最上位バイトがあります-ビッグエンディアン。
代わりにリトルエンディアンを使用してロードしようとした場合、ワイドレジスタの下部にバイトをロードし、次のバイトをステージングエリアにロードしてシフトし、ワイドレジスタの上部にポップする必要があります。または、ゲートのより複雑な配置を使用して、上位または下位バイトに選択的にロードできるようにします。
リトルエンディアンにしようとした結果、より多くのシリコン(スイッチとゲート)が必要になるか、より多くの操作が必要になります。
言い換えれば、昔のバックに見返りを得るという点で、あなたはほとんどのパフォーマンスと最小のシリコン面積のためにより多くのバングを得ました。
最近では、これらの考慮事項とほとんど無関係ですが、パイプラインの充満などのことはまだ少し大したことかもしれません。
s / wを書くことになると、リトルエンディアンのアドレス指定を使用する方が頻繁に簡単です。
(これは人生を作る。そして、ビッグエンディアンプロセッサは、ビット・イン・バイト単位でバイト順序とリトルエンディアンの観点からビッグエンディアンになりがち。しかし、いくつかのプロセッサは奇妙であり、ビッグエンディアンのビット順序だけでなく、バイト順を使用する非常にメモリマップされた周辺機器を追加するハードウェア設計者にとっては興味深いが、プログラマーにとっては他に何の影響もない。)
賢明に良い点を挙げました。別の問題があります。リトルエンディアンでは、次のことができます。
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として保存され、書かれるべきです:)
for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }
対応しmove.l data, num
ます。
私はいつも誰かがバイトを逆順で保存したいと思うのだろうか
10進数はビッグエンディアンで書かれています。また、英語での書き方も重要な数字から始め、次に重要なものから最も重要でないものへと書きます。例えば
1234
千二百三十四です。
これはビッグエンディアンが自然順序と呼ばれることもある方法です。
リトルエンディアンでは、この数は1、20、300、4000です。
ただし、加算や減算などの算術演算を実行するときは、最後から始めます。
1234
+ 0567
====
4と7から始めて、最低桁を書き、キャリーを覚えてください。次に、3と6などを追加します。加算、減算、または比較については、メモリを順番に読み取るロジックが既にある場合、数字が逆になっている場合、実装が簡単です。
この方法でビッグエンディアンをサポートするには、メモリを逆読みするロジックが必要です。または、レジスタでのみ動作するRISCプロセスがあります。;)
Intel x86 / Amd x64の設計の多くは歴史的なものです。
可変長のストレージと転送のみが関与し、複数の値を伴う算術演算が関与しない場合、LEは通常書き込みやすく、BEは読みやすくなります。
具体的な例として、intから文字列への変換(およびその逆)を考えてみましょう。
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を掛ける追加の係数が必要です。
したがって、値は1回だけ書き込まれますが、少なくとも1回は何度も読み取られるため、通常はストレージにBEを使用します。より単純な構造のために、私は通常、2回目に値を書き込んでも、LEに変換するためにルートに行き、結果を逆にします。
BEストレージの別の例としては、UTF-8エンコーディングなどがあります。