Odd decimal type behavior for ToString(IFormatProvider)

前端 未结 3 1201
南方客
南方客 2021-01-25 00:25
var numberFormat = new NumberFormatInfo();
numberFormat.NumberDecimalSeparator = \".\";
numberFormat.NumberDecimalDigits = 2;

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

Cons         


        
相关标签:
3条回答
  • 2021-01-25 00:47

    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/

    0 讨论(0)
  • 2021-01-25 01:06

    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.

    0 讨论(0)
  • 2021-01-25 01:11

    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));
    
    0 讨论(0)
提交回复
热议问题