リトルエンディアン形式の利点は何ですか?


140

Intelプロセッサー(およびおそらく他のプロセッサー)は、ストレージにリトルエンディアン形式を使用します。

なぜ誰かがバイトを逆順で保存したいのかといつも疑問に思います。この形式には、ビッグエンディアン形式よりも利点がありますか?


1
6502は初期の(最初の?)パイプラインプロセッサでした。パイプラインによるパフォーマンス関連の問題については、リトルエンディアンであるという主張を覚えているようです。助言がありますか?
Steve314

1
@ Steve314:私の答えは、リトルエンディアンは、パイプラインCPUでのパフォーマンスをどのように役立つかを説明します。programmers.stackexchange.com/q/95854/27874
マーティンVilcans

3
リトルエンディアン、ビッグエンディアン-どちらかを選択する必要があります。道路の左側または右側を運転するようなものです。

3
できれば6502やZ80などの「旧式」アーキテクチャ向けに、ASMでコードを作成することをお勧めします。これらがリトルエンディアンを使用する理由がすぐにわかります。ビッグエンディアンを使用するアーキテクチャーには、命令セットに特定の特性があり、代わりにその形式を優先します。それはarbitrary意的な決定ではありません!
ステファンポール

2
各バイトオーダーシステムには利点があります。リトルエンディアンのマシンでは、他のマシンを読み取ることなく、最下位バイトを最初に読み取ることができます。数値が奇数か偶数か(最後のビットは0)を簡単に確認できます。これは、この種のことに興味がある場合に便利です。ビッグエンディアンシステムは、人間がデータについて考えるのと同じ方法(左から右)でメモリにデータを保存するため、低レベルのデバッグが容易になります。
コライトゥゲイ

回答:


198

どちらの方法でも引数がありますが、1つのポイントは、リトルエンディアンシステムでは、32ビット、16ビット、または8ビット幅として取得されたメモリ内の特定の値のアドレスが同じであるということです。

つまり、メモリに2バイトの値がある場合:

0x00f0   16
0x00f1    0

'16'を16ビット値(ほとんどの32ビットシステムではc 'short')または8ビット値(一般にc 'char')として取得すると、使用するフェッチ命令のみが変更され、フェッチするアドレスは変更されません。から。

ビッグエンディアンシステムでは、上記のレイアウトは次のとおりです。

0x00f0    0
0x00f1   16

ポインタをインクリメントしてから、新しい値に対してより狭いフェッチ操作を実行する必要があります。

つまり、要するに、「リトルエンディアンシステムでは、キャストは無操作です。」


3
もちろん、あなたが読まなかった上位バイトは合理的に無視できると仮定します(たとえば、とにかくゼロであることがわかっています)。
Steve314

10
@ Steve314:2の補数のシステム(大部分のシステム)で32ビットから16ビット(たとえば)にCダウンキャストしている場合、無視するためにバイトをゼロにする必要はありません。それらの価値に関係なく、私はそれらを無視し、C標準およびプログラマーの期待に準拠したままにすることができます。

9
@Stritzinger-コンパイラーによって生成されたアセンブリ/マシンコードについて話しているが、これは移植性がない。コンパイルする高レベルの言語コードは移植性があり、すべてのopが行うように、異なるアーキテクチャで異なる操作にコンパイルするだけです。
賢明な

7
ビッグエンディアンアーキテクチャでは、ポインターは、参照しているものが何であれ、まったく同じ利点があるのではなく、先頭ではなく末尾を指す可能性があるため、この引数を購入しません。
dan_waterworth

4
@dan_waterworthは完全ではありません-たとえば、Cのポインター算術規則と、同じポインターのキャストをインクリメントまたはデクリメントするとどうなるかを覚えておいてください。複雑さを変えることはできますが、それをなくすことはできません。
ジムワイズ

45

なぜ誰かがバイトを逆順で保存したいのかといつも疑問に思います。

ビッグエンディアンとリトルエンディアンは、人間の観点からは「通常の順序」と「逆の順序」に過ぎず、これらすべてが真である場合にのみ...

  1. 画面または紙で値を読んでいます。
  2. 左側に低いメモリアドレスを配置し、右側に高いメモリアドレスを配置します。
  3. 上位のニブルを左側に、または最上位ビットを左側に、バイナリで16進数で書いています。
  4. 左から右に読みます。

これらはすべて人間の慣習であり、CPUにはまったく関係ありません。#1と#2を保持し、#3を反転すると、右から左に書かれたアラビア語またはヘブライ語を読む人にとって、リトルエンディアンは「完全に自然」に見えます。

そして、ビッグエンディアンを作る他の人間の慣習があります。

  • 「上位」(最上位)バイトは「上位」メモリアドレスにある必要があります。

私が主に68KとPowerPCをプログラミングしていた頃、ビッグエンディアンは「正しい」と考え、リトルエンディアンは「間違っている」と考えていました。しかし、ARMとIntelの仕事を増やして以来、リトルエンディアンに慣れてきました。本当に関係ありません。


30
実際、数字はアラビア語とヘブライ語で[最上位桁]から[最下位桁]まで右に書かれています。
Random832

5
次に、なぜバイト内のビットが「ビッグエンディアン」形式で保存されるのですか?なぜ一貫していないのですか?
-tskuzzy

11
それらはそうではありません-慣例により、ビット0が最下位、ビット7が最上位です。さらに、ビットは個別にアドレス指定できないため、通常、バイト内のビットに順序を付けることはできません。もちろん、特定の通信プロトコルまたはストレージメディアで物理的な順序を持っている可能性がありますが、低レベルのプロトコルまたはハードウェアレベルで作業している場合を除き、この順序を気にする必要はありません。
スチュワート

3
BlueRaja:紙に書く慣例によってのみ。これには、CPUアーキテクチャとの共通点はありません。バイトを7-0 MSB-LSBの代わりに0-7 LSB-MSBとして書き込むことができ、アルゴリズムの観点からは何も変わりません。
SF。

2
@SF .:「短く押して、短いもの以外はポップする」とにかく、あなたは驚きを得るでしょう。たとえば、ポップしないバイトをプッシュしてスタックを破損していない場合でも、その逆も同様です...たとえば、x86(32ビット)は、スタックを実際にdwordに揃えて、スタックポインターが4の倍数でないと、アライメントの問題が発生する可能性があります。そして、たとえそうでなかったとしても、スタッフは一度に単語/ dword / qword /などをプッシュします。したがって、下位バイトは、ポップしたときに最初に取得するバイトのままです。
cHao

41

OK、説明した理由は次のとおりです。加算と減算

マルチバイト数を加算または減算する場合、最下位バイトから開始する必要があります。たとえば、2つの16ビット数を追加する場合、最下位バイトから最上位バイトへのキャリーが発生する可能性があるため、キャリーがあるかどうかを確認するには最下位バイトから開始する必要があります。これは、ロングハンド加算を行うときに右端の数字から開始するのと同じ理由です。左から始めることはできません。

メモリから連続してバイトをフェッチする8ビットシステムを検討します。最初に最下位バイトをフェッチする場合、最上位バイトがメモリからフェッチされているに加算開始できます。この並列性が、システムなどのリトルエンディアンでパフォーマンスが優れている理由です。両方のバイトがメモリからフェッチされるまで待機するか、逆の順序でフェッチする必要がある場合、時間がかかります。

これは古い8ビットシステムです。最近のCPUでは、バイトオーダーが違いを生むのではないかと疑っています。リトルエンディアンは歴史的な理由でのみ使用しています。


3
ああ-だから、私が大きな整数にリトルエンディアンのチャンク順序を使用するのとほぼ同じ理由です。私はそれを解決すべきだった。人々は本当にサイバネティックスに取り組む必要があります -私の脳はすでにいくつかの交換部品といくつかの抜本的なアップグレードを必死に必要としているので、私は永遠に待つことができません!
Steve314

2
思考-6502はハードウェアで16ビット演算をあまりしませんでした-結局のところ、それは8ビットプロセッサでした。ただし、16ビットのベースアドレスに対して8ビットの符号付きオフセットを使用て、相対アドレス指定を行いました。
Steve314

2
この考えは、(Steve314が言ったように)多倍長整数の算術演算では依然として重要ですが、単語レベルでは重要であることに注意してください。現在、ほとんどの操作はプロセッサのエンディアンの影響を直接受けません。GMPで行われているように、ビッグエンディアンシステムで最下位ワードを最初に保存できます。リトルエンディアンプロセッサは、一度に1バイトを読み取ることで簡単に実行できる少数の操作(たとえば、文字列変換など)に利点があります。これは、リトルエンディアンシステムでのみ、そのような数値のバイト順序が正しいためです。
vinc17 14

16ビットメモリバスを備えた32ビットARMプロセッサや8ビットデータバスを備えた8088のように、メモリ帯域幅が制限されている場合、リトルエンディアンプロセッサには利点があります。上位半分を待っている間/サブ/ MULはそれで...追加
phuclv

13

8ビットプロセッサの方が確かに効率的でした。別のコードや追加の値をバッファリングする必要なく、8ビットまたは16ビットの操作を実行できました。

一度に1バイトを処理している場合、いくつかの追加操作にとってはなお良いです。

しかし、ビッグエンディアンがより自然である理由はありません-英語では13(リトルエンディアン)と23(ビッグエンディアン)を使用します


1
ビッグエンディアンは、バイトを再配置する必要がないため、人間にとって実際に簡単です。たとえば、PCの場合0x1234567878 56 34 12BEシステムの場合と同じように保存されます12 34 56 78(バイト0は左側に、バイト3は右側にあります)。数が大きいほど(ビット単位で)、より多くのスワップが必要になることに注意してください。WORDには1つのスワップが必要です。DWORD、2パス(合計3回のスワップ); QWORD 3パス(合計7)など。つまり、(bits/8)-1スワップ。別のオプションは、それらを前方後方の両方で読み取ることです(各バイトを前方に読み取りますが、#全体を後方にスキャンします)。
Synetech

百十三十進数は、ミドルエンディアン、またはビッグエンディアンのいずれかであり、「十三」は本質的に10進数ではない1桁です。数字を綴るとき、数字に使用する定数ベースの規則から若干の逸脱がありますが、これらの特殊なケースを取り除くと、残りはビッグエンディアン-数百万の前、数千の前など
Steve314

@ Synetech-幸いなことに、コンピューターは人間の読み方を気にする必要はありません。これは、NANDフラッシュは「OTために優れていると主張のようなものだ
マーティンベケットを

1
@ Steve314、綴られた数字の単語は重要ではありません。プログラミング時に使用するのは数値の読み出しです。マーティン、人間が数字を読む方法を気にする必要のあるコンピューターはありませんが、人間が数字を読みやすい場合は、プログラミング(または他の関連する作業)が簡単になり、いくつかの欠陥やバグを軽減または回避できます。
Synetech

@ steve314デンマーク語では、「95」は「fem halvfems」と発音されます(5、および4半半)。
バティーン

7

日本の日付規則は「ビッグエンディアン」-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ビット幅のチャンクはリトルエンディアン順に格納されます。その理由は...

  1. 多くのアルゴリズムは、最も重要ではない終わりから自然に機能し始め、それらの終わりが一致することを望んでいます。たとえば、キャリーはより多くの有効数字に伝播するため、最下位から開始することは理にかなっています。

  2. 値を大きくしたり小さくしたりすることは、最後にチャンクを追加/削除することを意味します-チャンクを上下に移動する必要はありません。メモリの再割り当てのためにコピーが必要な場合がありますが、頻繁ではありません。

もちろん、これはプロセッサとは明らかに関係ありません-CPUがハードウェアのビッグ整数サポートで作成されるまで、それは純粋にライブラリのものです。


7

なぜこれが行われるのか、結果に関する多くのことを誰も答えていない。

特定のクロックサイクルでメモリから1バイトをロードできる8ビットプロセッサを考えます。

さて、もしあなたが持っている唯一の16ビットレジスターに(例えば)16ビットの値をロードしたいのであれば-つまりプログラムカウンター、それをする簡単な方法は:

  • フェッチ位置からバイトをロードします
  • そのバイトを左に8桁シフトします
  • メモリフェッチの場所を1増やします
  • 次のバイトを(レジスタの下位部分に)ロードします

結果:フェッチ位置をインクリメントするだけで、より広いレジスタの下位部分にロードするだけで、左にシフトできる必要があるだけです。(もちろん、右へのシフトは他の操作に役立つため、これはちょっとしたサイドショーです。)

この結果、16ビット(ダブルバイト)のものは、Most..Leastの順に格納されます。つまり、小さいアドレスには最上位バイトがあります-ビッグエンディアン。

代わりにリトルエンディアンを使用してロードしようとした場合、ワイドレジスタの下部にバイトをロードし、次のバイトをステージングエリアにロードしてシフトし、ワイドレジスタの上部にポップする必要があります。または、ゲートのより複雑な配置を使用して、上位または下位バイトに選択的にロードできるようにします。

リトルエンディアンにしようとした結果、より多くのシリコン(スイッチとゲート)が必要になるか、より多くの操作が必要になります。

言い換えれば、昔のバックに見返りを得るという点で、あなたはほとんどのパフォーマンスと最小のシリコン面積のためにより多くのバングを得ました。

最近では、これらの考慮事項とほとんど無関係ですが、パイプラインの充満などのことはまだ少し大したことかもしれません。

s / wを書くことになると、リトルエンディアンのアドレス指定を使用する方が頻繁に簡単です。

(これは人生を作る。そして、ビッグエンディアンプロセッサは、ビット・イン・バイト単位でバイト順序とリトルエンディアンの観点からビッグエンディアンになりがち。しかし、いくつかのプロセッサは奇妙であり、ビッグエンディアンのビット順序だけでなく、バイト順を使用する非常にメモリマップされた周辺機器を追加するハードウェア設計者にとっては興味深いが、プログラマーにとっては他に何の影響もない。)


3

賢明に良い点を挙げました。別の問題があります。リトルエンディアンでは、次のことができます。

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として保存され、書かれるべきです:)


1
これは、CPUに固有の形式で作業する方が高速/簡単であることを証明しているだけです。それが良いかどうかについては何も言わない。同じことがビッグエンディアンにも当てはまります。ビッグエンディアンCPUにfor(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }対応しmove.l data, numます。
マーティンVilcans

@martin:1つの少ない減算は、私の本の中で優れている
CemのKalyoncu

コンパイラーはとにかくループを展開するので、それは本当に重要ではありません。いずれにせよ、多くのCPUにはこの問題を処理するためのバイトスワッピング命令があります。
マーティンVilcans

私はビッグエンディアンでbcozに同意しません、私は{num << = 8; num | = data [i]; }少なくとも、mulを使用して左シフトカウントを計算する必要はありません
HayriUğurKoltuk

@ali:コードは、私が書いたとおりの操作を行い、ビッグエンディアンでは機能しません。
CemのKalyoncu

1

私はいつも誰かがバイトを逆順で保存したいと思うのだろうか

10進数はビッグエンディアンで書かれています。また、英語での書き方も重要な数字から始め、次に重要なものから最も重要でないものへと書きます。例えば

1234

千二百三十四です。

これはビッグエンディアンが自然順序と呼ばれることもある方法です。

リトルエンディアンでは、この数は1、20、300、4000です。

ただし、加算や減算などの算術演算を実行するときは、最後から始めます。

  1234
+ 0567
  ====

4と7から始めて、最低桁を書き、キャリーを覚えてください。次に、3と6などを追加します。加算、減算、または比較については、メモリを順番に読み取るロジックが既にある場合、数字が逆になっている場合、実装が簡単です。

この方法でビッグエンディアンをサポートするには、メモリを逆読みするロジックが必要です。または、レジスタでのみ動作するRISCプロセスがあります。;)

Intel x86 / Amd x64の設計の多くは歴史的なものです。


0

ビッグエンディアンは、一部の操作に役立ちます(オクテット長が等しい「ビッグナム」の比較を念頭に置いてください)。他のリトルエンディアン(おそらく2つの「ビッグナム」を追加)。最終的には、CPUハードウェアの設定によって異なりますが、通常はどちらかです(一部のMIPSチップは、IIRC、ブート時にLEまたはBEに切り替え可能)。


0

可変長のストレージと転送のみが関与し、複数の値を伴う算術演算が関与しない場合、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エンコーディングなどがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.