问题
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");
In console: 10.00 10 True
Why is it different? More important, how do I call ToString() to ensure same output no matter how a variable is initialized?
回答1:
The NumberDecimalDigits
property is used with the "F"
and "N"
standard format strings, not the ToString
method called without a format string.
You can use:
Console.WriteLine(a.ToString("N", numberFormat));
回答2:
The question of how to make it output consistently has been answered, but here is why they output differently in the first place:
A decimal
value contains, internally, fields for a scale and a coefficient. In the case of 10M
, the value encoded has a coefficient of 10 and a scale of 0:
10M = 10 * 10^0
In the case of 10.00M
, the value encoded has a coefficient of 1000 and a scale of 2:
10.00M = 1000 * 10^(-2)
You can sort of see this by inspecting the values in-memory:
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") + " ");
}
}
Outputs
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 is 10 in hex, and 0x3E8 is 1000 in hex)
This behaviour is outlined in section 2.4.4.3 of the C# spec:
A real literal suffixed by M or m is of type decimal. For example, the literals 1m, 1.5m, 1e10m, and 123.456M are all of type decimal. This literal is converted to a decimal value by taking the exact value, and, if necessary, rounding to the nearest representable value using banker's rounding (§4.1.7). Any scale apparent in the literal is preserved unless the value is rounded or the value is zero (in which latter case the sign and scale will be 0). Hence, the literal 2.900m will be parsed to form the decimal with sign 0, coefficient 2900, and scale 3.
回答3:
Try this:
Console.WriteLine(String.Format("{0:0.00}", a));
Console.WriteLine(String.Format("{0:0.00}", b));
The output will have always 2 decimal cases. More examples here:
http://www.csharp-examples.net/string-format-double/
来源:https://stackoverflow.com/questions/13810922/odd-decimal-type-behavior-for-tostringiformatprovider