我有两小段代码。在我看来,它们应该产生相同的字符串,但它们不会:
(1.23M * 100M).ToString()
结果:
123,00
和
(123M).ToString()
结果:
123
我非常简单的问题是:有人可以解释为什么会发生这种(奇怪的?)行为吗?
答案 0 :(得分:4)
decimal
类型由一个按因子10缩放的整数表示。来自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
我们可以看到编译器实际上优化了乘法,并为第一种情况插入了一个构造单个十进制实例的调用。这两个实例使用不同的表示。它们基本上就是我上面所描述的。
答案 1 :(得分:3)
它们是两个不同的值,按位。与double
不同,decimal
不会自动规范化 - 看起来它保留了一点上有两位小数的信息。你可以在没有乘法的情况下看到完全相同的差异:
Console.WriteLine(123m)
Console.WriteLine(123.00m);
根据保留的小数位数,对于decimal
值的操作结果到底是如何执行的,文档有点不清楚(从我所看到的)。 (我不会惊讶地发现它已在某处标准化了......)