简体   繁体   English

ToString(IFormatProvider)的奇数十进制类型行为

[英]Odd decimal type behavior for 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");

In console: 10.00 10 True 在控制台中:10.00 10是

Why is it different? 为什么不同? More important, how do I call ToString() to ensure same output no matter how a variable is initialized? 更重要的是,无论如何初始化变量,如何调用ToString()以确保相同的输出?

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. decimal值在内部包含decimal和系数的字段。 In the case of 10M , the value encoded has a coefficient of 10 and a scale of 0: 10M的情况下,编码的值的系数为10,小数位数为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.00M为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) (十六进制的0xA为10,十六进制的0x3E8为1000)

This behaviour is outlined in section 2.4.4.3 of the C# spec: C#规范的第2.4.4.3节概述了此行为:

A real literal suffixed by M or m is of type decimal. 带M或m后缀的实数是十进制类型。 For example, the literals 1m, 1.5m, 1e10m, and 123.456M are all of type decimal. 例如,文字1m,1.5m,1e10m和123.456M均为十进制类型。 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). 通过获取确切值,将该文字转换为十进制值,并在必要时使用银行家舍入法将其舍入为最接近的可表示值(第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). 除非值四舍五入或值为零(在后一种情况下,符号和小数位将为0),否则保留字面上明显的小数位数。 Hence, the literal 2.900m will be parsed to form the decimal with sign 0, coefficient 2900, and scale 3. 因此,将解析字面量2.900m以形成带符号0,系数2900和小数位3的小数。

The NumberDecimalDigits property is used with the "F" and "N" standard format strings, not the ToString method called without a format string. NumberDecimalDigits属性与"F""N"标准格式字符串一起使用,而不是与没有格式字符串的ToString方法一起使用。

You can use: 您可以使用:

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

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. 输出将始终有2个十进制大小写。 More examples here: 这里有更多示例:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM