我有两段小代码。在我看来,它们应该产生相同的字符串,但它们不会:
(1.23M * 100M).ToString()
结果是:
123,00
和
(123M).ToString()
结果是:
123
我非常简单的问题是:有人能解释一下为什么会发生这种(奇怪的?)行为吗?
该decimal
类型由一个按 10 倍缩放的整数表示。从文档中decimal
:
缩放因子还保留 Decimal 数字中的任何尾随零。尾随零不会影响算术或比较运算中的 Decimal 数的值。但是,如果应用了适当的格式字符串,ToString 方法可能会显示尾随零。
使用GetBits
您可以看到它123.00M
表示为 12300 / 10 2而表示123M
为 123 / 10 0。
编辑
我采用了一个简单的程序来演示这个问题:
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
我们可以看到,编译器实际上优化了乘法,并为第一种情况插入了对构造单个十进制实例的调用。这两个实例使用不同的表示。它们基本上就是我上面描述的。
按位,它们是两个不同的值。与double
,不同,decimal
它不会自动规范化 - 它似乎保留了在某一时刻您有两位小数的信息。您可以在没有乘法的情况下看到完全相同的差异:
Console.WriteLine(123m)
Console.WriteLine(123.00m);
decimal
就保留了多少小数位而言,文档有点不清楚(从我所见)关于值操作的结果是如何执行的。(得知它在某处被标准化,我不会感到惊讶......)