2 つの小さなコードがあります。私の意見では、それらは同じ文字列になるはずですが、そうではありません:
(1.23M * 100M).ToString()
結果:
123,00
と
(123M).ToString()
結果:
123
私の非常に単純な質問は、誰かがなぜこの (奇妙な?) 動作が起こるのか説明してもらえますか?
型は、decimal
10 倍にスケーリングされた整数で表されますdecimal
。
スケーリング係数は、10 進数の末尾のゼロも保持します。末尾のゼロは、算術演算または比較演算の 10 進数の値には影響しません。ただし、適切な書式文字列が適用されている場合、ToString メソッドによって末尾のゼロが明らかになることがあります。
を使用すると、 12300 / 10 2として表され、123 / 10 0GetBits
として表されることがわかります。123.00M
123M
編集
この問題を説明する簡単なプログラムを作成しました。
class Program
{
static void Main(string[] args)
{
Console.WriteLine((1.23M * 100M).ToString());
Console.WriteLine((123M).ToString());
}
}
生成された IL を確認しました。
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 51 (0x33)
.maxstack 6
.locals init ([0] valuetype [mscorlib]System.Decimal CS$0$0000)
IL_0000: nop
IL_0001: ldc.i4 0x300c
IL_0006: ldc.i4.0
IL_0007: ldc.i4.0
IL_0008: ldc.i4.0
IL_0009: ldc.i4.2
IL_000a: newobj instance void [mscorlib]System.Decimal::.ctor(int32,
int32,
int32,
bool,
uint8)
IL_000f: stloc.0
IL_0010: ldloca.s CS$0$0000
IL_0012: call instance string [mscorlib]System.Decimal::ToString()
IL_0017: call void [mscorlib]System.Console::WriteLine(string)
IL_001c: nop
IL_001d: ldc.i4.s 123
IL_001f: newobj instance void [mscorlib]System.Decimal::.ctor(int32)
IL_0024: stloc.0
IL_0025: ldloca.s CS$0$0000
IL_0027: call instance string [mscorlib]System.Decimal::ToString()
IL_002c: call void [mscorlib]System.Console::WriteLine(string)
IL_0031: nop
IL_0032: ret
} // end of method Program::Main
コンパイラが実際に乗算を最適化し、最初のケースで単一の 10 進インスタンスの構成への呼び出しを挿入したことがわかります。2 つのインスタンスは異なる表現を使用しています。それらは基本的に私が上で説明したものです。
これらは、ビット単位の 2 つの異なる値です。とは異なりdouble
、decimal
は自動的に正規化されません。ある時点で小数点以下 2 桁だったという情報が保持されているようです。乗算なしでまったく同じ違いを見ることができます。
Console.WriteLine(123m)
Console.WriteLine(123.00m);
decimal
ドキュメントは、保持される小数点以下の桁数に関して、値に対する操作の結果がどのように実行されるかについて、(私が見る限り) やや不明確です。(どこかで標準化されていることを知っても驚かないでしょう...)