LoginSignup
4

More than 5 years have passed since last update.

[C#] decimal型の数値はメモリ上での並び方がちょっと独特です

Last updated at Posted at 2015-09-10

こんにちはー!ニアです。

先ほど書いた「浮動小数点型の数値はメモリ上でどのように格納されているのか」では、浮動小数点型(float型など)の数値がメモリ上でどのように格納されているかを調べていきました。

では、.NET Frameworkのdecimal型の場合はどうでしょう・・・

decimal型の内部表現

1. メモリ上での並び順を調べる

C#で数値のメモリ上での並び順を調べる時は、BitConverterクラスのGetBytesメソッドを使用します。取得したbyte型配列の要素の並び方は、メモリ上での並び方と同じです。

byte-order.cs
using System;
using System.Linq;

class Program {
    static void Main( string[] args ) {

        Console.WriteLine( "バイトオーダー : {0}方式\n", BitConverter.IsLittleEndian ? "リトルエンディアン" : "ビッグエンディアン" );

        int i = 0x01020304;
        byte[] ib = BitConverter.GetBytes( i );
        Console.WriteLine( "int型" );
        Console.WriteLine( "元の値 : {0:X8}", i );
        Console.WriteLine( "メモリ上の値 : {0}\n", string.Join( " ", ib.Select( _ => _.ToString( "X2" ) ) ) );

        float f = 1.024f;
        byte[] fb = BitConverter.GetBytes( f );
        Console.WriteLine( "float型" );
        Console.WriteLine( "元の値 : {0}", f );
        Console.WriteLine( "内部表現の値 : {0:X8}", BitConverter.ToInt32( fb, 0 ) );
        Console.WriteLine( "メモリ上の値 : {0}", string.Join( " ", fb.Select( _ => _.ToString( "X2" ) ) ) );
    }
}

◆ 実行結果

バイトオーダー : リトルエンディアン方式

int型
元の値 : 01020304
メモリ上の値 : 04 03 02 01

float型
元の値 : 1.024
内部表現の値 : 3F83126F
メモリ上の値 : 6F 12 83 3F

decimalの場合

しかし、BitConverterクラスのGetBytesメソッドには、decimal型用のオーバーロードがありません。代わりに、ポインタを使って直接変数にアクセスしていきます。

内部表現はdecimalのGetBitsメソッドを使って取得します。

byte-order-decimal.cs
using System;
using System.Linq;

class Program {
    static unsafe void Main( string[] args ) {

        decimal d = -0.4972397250606885993375668763m;
        Console.WriteLine( "元の値 : {0}", d );
        Console.WriteLine( "内部表現の値 : {0}", string.Join( " ", decimal.GetBits( d ).Reverse().Select( _ => _.ToString( "X8" ) ) ) );

        // byte型ポインタにdecimal型変数のアドレスを代入します。
        byte* pdb = ( byte* )&d;
        // byte型ポインタからiバイト分だけオフセットして、byte型の値を取得します。
        byte[] db = new byte[16];
        for( int i = 0; i < 16; i++ ) db[i] = *( pdb + i );
        Console.WriteLine( "メモリ上での並び順 : {0}", string.Join( " ", db.Select( _ => _.ToString( "X2" ) ) ) );

    }
}

※アンセーフコードを扱うので、コンパイル時に「/unsafe」オプションを付けます。

◆ 実行結果
※使用したコンピューターのCPUのバイトオーダーはリトルエンディアン方式です。

元の値 : -0.4972397250606885993375668763
内部表現の値 : 801C0000 10111213 14151617 18191A1B
メモリ上での並び順 : 00 00 1C 80 13 12 11 10 1B 1A 19 18 17 16 15 14

2. decimal型の数値における、メモリ上での並び方

実行結果にあるdecimal型の数値の内部表現とメモリ上での並び順を見比べると、内部表現の値を4バイトずつ分割し、各要素の中はバイト単位で逆順に並んでいることが分かります。

しかし、4バイトずつ分割したものについて、内部表現での並び順を上位から3→2→1→0(※GetBitsメソッドで取得した時の要素のインデックスに合わせています)とすると、メモリ上では3→2→0→1となっています。

littleE-decimal.png

decimal型の謎は深い・・・

それでは、See you next!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4