C#中“decimal”类型的有趣行为

时间:2011-06-27 18:51:57

标签: c# decimal

如果我们将填充声明为const十进制,则填充不起作用。

mymoney = 1.2,你的钱= 1.20,如何解释这种行为?

class Program
{
    static void Main(string[] args)
    {
        decimal balance = 1.2m;

        const decimal ConstPadding = 0.00m;

        decimal padding = 0.00m;
        decimal mymoney = decimal.Round(balance + ConstPadding, 2);
        decimal yourmoney =  decimal.Round(balance + padding, 2);

        Console.WriteLine(mymoney); // 1.2
        Console.WriteLine(yourmoney);  //1.20
    }
}

5 个答案:

答案 0 :(得分:15)

编译器“知道”将值“0”添加到“不应该”更改值 - 因此它会优化此值。现在可以说,鉴于十进制加法的性质,这是一个无效的优化,但如果你看一下生成的代码,你会发现mymoney的计算不涉及添加。

我不认为我会尝试使用添加0.00米作为确保特定比例的方法,说实话。您可以创建自己的代码以使用decimal.GetBitsthe constructor performing the reverse operation来强制执行扩展 - 但我认为它不会非常好。

你肯定需要这个“两位小数”形式作为中间值,还是仅用于演示?如果是后者,我会查看格式字符串。

答案 1 :(得分:4)

作为Jon的答案的伴奏,下面是您的代码生成的IL。正如他所提到的,mymoney从未被添加过。

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       61 (0x3d)
  .maxstack  6
  .locals init ([0] valuetype [mscorlib]System.Decimal balance,
           [1] valuetype [mscorlib]System.Decimal padding,
           [2] valuetype [mscorlib]System.Decimal mymoney,
           [3] valuetype [mscorlib]System.Decimal yourmoney)
  IL_0000:  nop
  IL_0001:  ldc.i4.s   12
  IL_0003:  ldc.i4.0
  IL_0004:  ldc.i4.0
  IL_0005:  ldc.i4.0
  IL_0006:  ldc.i4.1
  IL_0007:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32,
                                                                     int32,
                                                                     int32,
                                                                     bool,
                                                                     uint8)
  IL_000c:  stloc.0
  IL_000d:  ldc.i4.0
  IL_000e:  ldc.i4.0
  IL_000f:  ldc.i4.0
  IL_0010:  ldc.i4.0
  IL_0011:  ldc.i4.2
  IL_0012:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32,
                                                                     int32,
                                                                     int32,
                                                                     bool,
                                                                     uint8)
  IL_0017:  stloc.1
  IL_0018:  ldloc.0
  IL_0019:  ldc.i4.2
  IL_001a:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::Round(valuetype [mscorlib]System.Decimal,
                                                                                          int32)
  IL_001f:  stloc.2
  IL_0020:  ldloc.0
  IL_0021:  ldloc.1
  IL_0022:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Addition(valuetype [mscorlib]System.Decimal,
                                                                                                valuetype [mscorlib]System.Decimal)
  IL_0027:  ldc.i4.2
  IL_0028:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::Round(valuetype [mscorlib]System.Decimal,
                                                                                          int32)
  IL_002d:  stloc.3
  IL_002e:  ldloc.2
  IL_002f:  call       void [mscorlib]System.Console::WriteLine(valuetype [mscorlib]System.Decimal)
  IL_0034:  nop
  IL_0035:  ldloc.3
  IL_0036:  call       void [mscorlib]System.Console::WriteLine(valuetype [mscorlib]System.Decimal)
  IL_003b:  nop
  IL_003c:  ret
} // end of method Program::Main

要生成IL(即如果您希望将来查看),只需从VS命令提示符运行ILDASM,然后加载可执行文件并双击要查看的方法。

答案 2 :(得分:2)

balance + ConstPadding == balance

因为ConstPadding为零!

你应该 -

Console.WriteLine(yourmoney.ToString("0.00"));  //1.20

答案 3 :(得分:1)

使用常量填充的求和运算完全被排除在MSIL之外,但它存在于非常量字段中。 遗憾的是,我无法找到任何对FCallAddSub函数的引用,但那是“优化”调用的那个。

答案 4 :(得分:1)

如果我没有弄错,编译器没有使用常数十进制进行加法,因为它是零。

很快会发布证据。

Jon Skeet在上面的答案中证明了这一点。