2013-07-07 3 views
4

У меня есть два небольших фрагмента кода. На мой взгляд, они должны привести к одной и той же строки, но они этого не делают:Почему два десятичных знака с одинаковым значением, отформатированным по-разному

(1.23M * 100M).ToString() 

приводит:

123,00 

и

(123M).ToString() 

результатов в:

123 

Мой очень простой вопрос: может som кто-нибудь объяснит мне, почему это (странное?) поведение происходит?

ответ

3

Это два разных значения, побитовые. В отличие от double, decimal не автоматически нормализуется - похоже, что он сохранил информацию о том, что в какой-то момент у вас было два десятичных знака. Вы можете увидеть точно такую ​​же разницу без умножения:

Console.WriteLine(123m) 
Console.WriteLine(123.00m); 

документация несколько неясно (от того, что я могу видеть), как именно выполняется результат операций по decimal значений, в плане того, как много знаков после запятой сохраняются. (Я не был бы удивлен, узнав, что это стандартизировано где-то ...)

+1

Существует документация по этому вопросу, но, к сожалению, фактическое поведение не соответствует 100% спецификации. –

4

Тип decimal представлен целым числом масштабируется с коэффициентом 10. Из документации decimal:

Масштабирование фактор также сохраняет любые конечные нули в десятичном числе. Конечные нули не влияют на значение десятичного числа в арифметических или сравнительных операциях. Однако конечные нули могут быть обнаружены методом ToString, если применяется соответствующая строка формата.

Использование GetBits вы можете увидеть, что 123.00M представлен как 12300/10 в то время как 123M составляет 123/10 .

Редактировать

Я взял простую программу, которая demostrates вопрос:

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 

Мы можем видеть, что компилятор на самом деле оптимизированы отмените умножение и вставили вызов конструкции одного десятичного экземпляра для первого случая. Два экземпляра используют разные представления. В основном это то, что я описал выше.

Смежные вопросы