16

以下のコードで、なぜ X と Y は私が直感的に考えたものとは異なる値を取るのでしょうか?

0 から 7 のバイトがバッファに書き込まれた場合、結果の long は同じ順序でバイトを持つべきではありませんか? 長い値を逆順に読んでいるようなものです。

x    0x0706050403020100    long
y    0x0706050403020100    long
z    0x0001020304050607    long

MemoryStream ms = new MemoryStream();
byte[] buffer = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
ms.Write(buffer, 0, buffer.Length);
ms.Flush();
ms.Position = 0;

BinaryReader reader = new BinaryReader(ms);
long x = reader.ReadInt64();
long y = BitConverter.ToInt64(buffer, 0);
long z = BitConverter.ToInt64(buffer.Reverse<byte>().ToArray<byte>(), 0);

byte[] xbytes = BitConverter.GetBytes(x);
byte[] ybytes = BitConverter.GetBytes(y);
byte[] zbytes = BitConverter.GetBytes(z);

(.NETだけでなく、この質問に何をタグ付けすればよいかわかりません。)


BitConverter.IsLittleEndian

は偽です。コンピューターがビッグ エンディアンの場合、これはなぜ起こりますか?

  • これは Windows 7 64 ビット マシンです。
  • Intel Core2 Quad Q9400 2.66 GHz LGA 775 95W クアッドコア プロセッサ モデル BX80580Q9400
  • SUPERMICRO MBD-C2SBX+-O LGA 775 Intel X48 ATX Intel マザーボード

このコードの結果 (Jason のコメントに応じて):

byte[] buffer = new byte[] { 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
long y = BitConverter.ToInt64(buffer, 1);
Console.WriteLine(BitConverter.IsLittleEndian);
Console.WriteLine(y);

結果:

False
506097522914230528
4

7 に答える 7

23

BinaryReader.ReadInt64設計上、リトルエンディアンです。ドキュメントから:

BinaryReaderは、このデータ型をリトルエンディアン形式で読み取ります。

BinaryReader.ReadInt64実際、 Reflectorを使用するためのソースを検査できます。

public virtual long ReadInt64() {
    this.FillBuffer(8);
    uint num = (uint) (((this.m_buffer[0] |
              (this.m_buffer[1] << 0x08)) |
              (this.m_buffer[2] << 0x10)) |
              (this.m_buffer[3] << 0x18));
    uint num2 = (uint) (((this.m_buffer[4] |
               (this.m_buffer[5] << 0x08)) |
               (this.m_buffer[6] << 0x10)) |
               (this.m_buffer[7] << 0x18));
    return (long) ((num2 << 0x20) | num);
}

BinaryReader.ReadInt64基盤となるマシンアーキテクチャに関係なく、リトルエンディアンとして読み取られることを示しています。

ここでBitConverter.ToInt64、基盤となるマシンのエンディアンを尊重するとします。リフレクターで見ることができます

public static unsafe long ToInt64(byte[] value, int startIndex) {
    // argument checking elided
    fixed (byte* numRef = &(value[startIndex])) {
        if ((startIndex % 8) == 0) {
            return *(((long*) numRef));
        }
        if (IsLittleEndian) {
            int num = (numRef[0] << 0x00) |
                      (numRef[1] << 0x08) |
                      (numRef[2] << 0x10) |
                      (numRef[3] << 0x18);
            int num2 = (numRef[4] << 0x00) |
                       (numRef[5] << 0x08) |
                       (numRef[6] << 0x10) |
                       (numRef[7] << 0x18);
            return (((long) ((ulong) num)) | (num2 << 0x20));
        }
        int num3 = (numRef[0] << 0x18) |
                   (numRef[1] << 0x10) |
                   (numRef[2] << 0x08) |
                   (numRef[3] << 0x00);
        int num4 = (numRef[4] << 0x18) |
                   (numRef[5] << 0x10) |
                   (numRef[6] << 0x08) |
                   (numRef[7] << 0x00);
        return (((long) ((ulong) num4)) | (num3 << 0x20));
}

したがって、ここで確認できるstartIndexのは、8を法とするゼロと合同である場合、アドレスで始まる8バイトから直接キャストが実行されるということnumRefです。このケースは、アライメントの問題のために特別に処理されます。コードの行

return *(((long *) numRef));

に直接変換します

    ldloc.0      ;pushes local 0 on stack, this is numRef
    conv.i       ;pop top of stack, convert to native int, push onto stack
    ldind.i8     ;pop address off stack, indirect load from address as long
    ret          ;return to caller, return value is top of stack

ldind.i8したがって、この場合の鍵は命令であることがわかります。CLIは、基盤となるマシンのエンディアンに依存しません。これにより、JITコンパイラがその問題を処理できるようになります。リトルエンディアンのマシンでは、ldind.i8より高いアドレスをより重要なビットにロードし、ビッグエンディアンのマシンでは、ldind.i8より高いアドレスをより重要でないバイトにロードします。したがって、この場合、エンディアンは適切に処理されます。

他のケースでは、静的プロパティの明示的なチェックがあることがわかりますBitConverter.IsLittleEndian。リトルエンディアンの場合、バッファはリトルエンディアンとして解釈され(メモリ{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }がロングとして解釈されるように0x0706050403020100)、ビッグエンディアンの場合、バッファはビッグエンディアンとして解釈されます(メモリ{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }がロングとして解釈されるように0x0001020304050607)。ですから、BitConverterそれはすべて、アンダーイリングマシンのエンディアンに帰着します。Windows7x64のIntelチップを使用していることに注意してください。Intelチップはリトルエンディアンです。BitConverterReflectorでは、の静的コンストラクターは次のように定義されていることに注意してください。

static BitConverter() {
    IsLittleEndian = true;
}

これは私のWindowsVistax64マシンにあります。(たとえば、XBox360の.NETCFでは異なる場合があります。)Windows7x64が異なる理由はありません。したがって、それは確かBitConverter.IsLittleEndianですfalseか?それはそうあるべきでtrueあり、したがってあなたが見ている振る舞いは正しいです。

于 2010-01-07T16:05:04.747 に答える
6

整数が最下位バイトから最初に格納されるリトルエンディアンマシンを使用しています。

于 2010-01-05T00:36:41.913 に答える
4

BinaryReader はリトル エンディアンの順序を想定しています: http://msdn.microsoft.com/en-us/library/system.io.binaryreader.readint64.aspx

于 2010-01-05T00:48:45.570 に答える
3

BitConverter.IsLittleEndian が false を返していることを完全に確信していますか?

そのメソッドのいずれかを使用する前に debugger-watch を介して検査すると、true を返す必要がある場合でも false になる可能性があります。

完全に確実にするために、コードを介して値を読み取ります。IsLittleEndian フィールド レポート falseも参照してください。

于 2011-05-27T18:54:41.980 に答える
2

バイトのエンディアンが気になる場合は、Jon Skeet がクラスを作成して、変換時にエンディアン順を選択できるようにしました。

C# のリトル エンディアンまたはビッグ エンディアンを参照してください。

于 2010-01-05T00:51:36.673 に答える
1

BitConverter実行中のマシンのエンディアンを使用します。ビッグ エンディアン番号を確保するには、 を使用しますIPAddress.HostToNetworkOrder。例えば:

IPAddress.HostToNetworkOrder(BitConverter.ToInt64(buffer))
于 2015-04-30T09:43:16.140 に答える