【发布时间】:2013-07-07 20:34:22
【问题描述】:
我有两段小代码。在我看来,它们应该产生相同的字符串,但它们不会:
(1.23M * 100M).ToString()
结果:
123,00
和
(123M).ToString()
结果:
123
我的一个非常简单的问题是:有人能解释一下为什么会发生这种(奇怪的?)行为吗?
【问题讨论】:
我有两段小代码。在我看来,它们应该产生相同的字符串,但它们不会:
(1.23M * 100M).ToString()
结果:
123,00
和
(123M).ToString()
结果:
123
我的一个非常简单的问题是:有人能解释一下为什么会发生这种(奇怪的?)行为吗?
【问题讨论】:
decimal 类型由一个缩放 10 倍的整数表示。来自decimal 的文档:
缩放因子还保留十进制数中的任何尾随零。尾随零不会影响算术或比较运算中的 Decimal 数的值。但是,如果应用了适当的格式字符串,ToString 方法可能会显示尾随零。
使用GetBits,您可以看到123.00M 表示为12300 / 102,而123M 表示为123 / 100。
编辑
我采用了一个简单的程序来演示这个问题:
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 值的操作结果究竟是如何执行的,就保留了多少小数位而言。 (得知它在某处被标准化,我不会感到惊讶......)
【讨论】: