Поведение нечетного десятичного типа для 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 десятичных знака. Больше примеров здесь: