2009/11/06

Decimal型のフォーマット

Decimal.GetBits メソッド (System)で解説されている。
Decimal 数値のバイナリ表現は、1 ビットの符号、96 ビットの整数、および整数値を除算し、小数部を指定するために使用するスケール ファクタから構成されます。スケール ファクタは黙示的に数値 10 になり、0 から 28 の範囲の指数で累乗されます。
戻り値は、4 要素の 32 ビット符号付き整数配列です。
この配列の 1 ~ 3 番目の要素は、96 ビット整数の下位 32 ビット、中位 32 ビット、および上位 32 ビットをそれぞれ格納しています。
4 番目の要素は、スケール ファクタと符号を格納しています。この要素は、次に示す部分から構成されています。
ビット 0 ~ 15 の下位ワードは未使用で、0 である必要があります。
ビット 16 ~ 23 には、0 から 28 までの範囲の指数部を格納する必要があります。この指数部は、整数を除算する 10 の累乗を示します。
ビット 24 ~ 30 は未使用で、0 である必要があります。
ビット 31 は符号を格納している必要があります。0 は正、1 は負を表します。
ビット形式では負の 0 と正の 0 が区別されます。これらの値はすべての演算で等値として扱われます。
Decimal.GetBits メソッドの戻り値はint[]でいまいち分かりにくいから確認用のコードを書いた(下手なコードだと思うけど良い方法を思いつけなかった)。
指数部が10の累乗なんだからSingle/Doubleとは違う分かりやすい値に変えた。
unsafe private static void PrintDecimal()
        {
            Func<Decimal, byte[]> toBytes = x =>
            {
                var p = (byte*)&x;
                var bytes = new byte[sizeof(Decimal)];
                for (var n = 0; n < bytes.Length; n++)
                    bytes[n] = *p++;
                return bytes;
            };
            WriteLine("Decimal:");
            WriteLine("0.0M", toBytes(0.0M));
            WriteLine("-0.0M", toBytes(-0.0M));
            WriteLine("0.12M", toBytes(0.12M));
            WriteLine("1.2M ", toBytes(1.2M));
            WriteLine("12M  ", toBytes(12M));
            WriteLine("120M ", toBytes(120M));
            WriteLine("1200M", toBytes(1200M));
            WriteLine("-0.12M", toBytes(-0.12M));
            WriteLine("-1.2M ", toBytes(-1.2M));
            WriteLine("-12M  ", toBytes(-12M));
            WriteLine("-120M ", toBytes(-120M));
            WriteLine("-1200M", toBytes(-1200M));
            WriteLine("Decimal.MinValue", toBytes(Decimal.MinValue));
            WriteLine("Decimal.MaxValue", toBytes(Decimal.MaxValue));
            WriteLine("");
        }


        private static void WriteLine(string value)
        {
            Console.WriteLine(value);
        }

        private static void WriteLine(string label, byte[] bytes)
        {
            Console.Write("{0}: 0x", label);
            var bs = (byte[])bytes.Clone();
            Array.Reverse(bs);
            foreach (var x in bs)
                Console.Write("{0:X2}", x);
            Console.WriteLine();
        }
ksksts / junk / source — bitbucket.org

結果は次の通り。0.0Mと-0.0Mの指数部は1になるみたい。
Decimal:
0.0M: 0x00000000000000000000000000010000
-0.0M: 0x00000000000000000000000080010000
0.12M: 0x000000000000000C0000000000020000
1.2M : 0x000000000000000C0000000000010000
12M  : 0x000000000000000C0000000000000000
120M : 0x00000000000000780000000000000000
1200M: 0x00000000000004B00000000000000000
-0.12M: 0x000000000000000C0000000080020000
-1.2M : 0x000000000000000C0000000080010000
-12M  : 0x000000000000000C0000000080000000
-120M : 0x00000000000000780000000080000000
-1200M: 0x00000000000004B00000000080000000
Decimal.MinValue: 0xFFFFFFFFFFFFFFFFFFFFFFFF80000000
Decimal.MaxValue: 0xFFFFFFFFFFFFFFFFFFFFFFFF00000000

0 件のコメント: