Поведение нечетного десятичного типа для ToString(IFormatProvider)

var numberFormat = new NumberFormatInfo();
numberFormat.NumberDecimalSeparator = ".";
numberFormat.NumberDecimalDigits = 2;

decimal a = 10.00M;
decimal b = 10M;

Console.WriteLine(a.ToString(numberFormat));
Console.WriteLine(b.ToString(numberFormat));
Console.WriteLine(a == b ? "True": "False");

В консоли: 10.00 10 True

Почему это отличается? Что еще более важно, как я вызываю ToString() для обеспечения одинакового вывода независимо от того, как инициализируется переменная?

3 ответа

Решение

NumberDecimalDigits свойство используется с "F" а также "N" строки стандартного формата, а не ToString Метод вызывается без строки формата.

Ты можешь использовать:

Console.WriteLine(a.ToString("N", numberFormat));

На вопрос о том, как сделать так, чтобы вывод выводился последовательно, был дан ответ, но вот почему они выводят по-разному в первую очередь:

decimal значение содержит внутри поля для масштаба и коэффициента. В случае 10M закодированное значение имеет коэффициент 10 и шкалу 0:

10M = 10 * 10^0

В случае 10.00M закодированное значение имеет коэффициент 1000 и масштаб 2:

10.00M = 1000 * 10^(-2)

Вы можете увидеть это, проверив значения в памяти:

unsafe
{
    fixed (decimal* array = new decimal[2])
    {
        array[0] = 10M;
        array[1] = 10.00M;
        byte* ptr = (byte*)array;

        Console.Write("10M:    ");
        for (int i = 0; i < 16; i++)
            Console.Write(ptr[i].ToString("X2") + " ");

        Console.WriteLine("");

        Console.Write("10.00M: ");
        for (int i = 16; i < 32; i++)
            Console.Write(ptr[i].ToString("X2") + " ");
    }
}

Выходы

10M:    00 00 00 00 00 00 00 00 0A 00 00 00 00 00 00 00
10.00M: 00 00 02 00 00 00 00 00 E8 03 00 00 00 00 00 00

(0xA равно 10 в гексах, а 0x3E8 равно 1000 в гексах)

Это поведение описано в разделе 2.4.4.3 спецификации C#:

Действительный литерал с суффиксом M или m имеет тип decimal. Например, литералы 1m, 1.5m, 1e10m и 123.456M имеют десятичный тип. Этот литерал преобразуется в десятичное значение, беря точное значение и, при необходимости, округляя до ближайшего представимого значения, используя округление банкира (§4.1.7). Любая шкала, видимая в литерале, сохраняется, если только значение не округлено или значение не равно нулю (в этом случае знак и масштаб будут равны 0). Следовательно, литерал 2.900m будет проанализирован для формирования десятичной дроби со знаком 0, коэффициентом 2900 и шкалой 3.

Попробуй это:

Console.WriteLine(String.Format("{0:0.00}", a)); 
Console.WriteLine(String.Format("{0:0.00}", b)); 

Выходные данные всегда будут иметь 2 десятичных знака. Больше примеров здесь:

http://www.csharp-examples.net/string-format-double/

Другие вопросы по тегам