使用异常规则格式化十进制值

时间:2015-07-22 00:47:02

标签: c# type-conversion decimal string-formatting

根据以下规则,将小数值转换为字符串的优雅方法是什么?

  1. 显示小数点前的所有数字。
  2. 不变地显示逗号代替小数点。
  3. 如果小数点后的部分非零,则仅显示有效数字,但最少为2。
  4. 示例:

    decimal       string
    ------------  ----------
    500000        500000,
    500000.9      500000,90
    500000.90     500000,90
    500000.900    500000,90
    500000.9000   500000,90
    500000.99     500000,99
    500000.999    500000,999
    500000.9999   500000,9999
    

    我可以通过将值转换为int来轻松显示小数点前的部分和逗号。但是处理小数点后部分的不同情况变得冗长乏味。

    如果有办法指定我只想要小数点后面的数字,但没有小数点,我就会有这个。像String.Format("{0:.00#}", value)这样的东西,只显示小数点。

3 个答案:

答案 0 :(得分:2)

我不会称之为漂亮,但它属于"它的作用"。

首先是实施,

B* pb = (B*)( ((char*)pa) +n);

和示例用法:

public static class FormatProviderExtensions
{
    public static IFormatProvider GetCustomFormatter(this NumberFormatInfo info, decimal d)
    {
        var truncated = Decimal.Truncate(d);

        if (truncated == d)
        {
            return new NumberFormatInfo
            {
                NumberDecimalDigits = 0,
                NumberDecimalSeparator = info.NumberDecimalSeparator,
                NumberGroupSeparator = info.NumberGroupSeparator
            };
        }

        // The 4th element contains the exponent of 10 used by decimal's 
        // representation - for more information see
        // https://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
        var fractionalDigitsCount = BitConverter.GetBytes(Decimal.GetBits(d)[3])[2];
        return fractionalDigitsCount <= 2
            ? new NumberFormatInfo
            {
                NumberDecimalDigits = 2,
                NumberDecimalSeparator = info.NumberDecimalSeparator,
                NumberGroupSeparator = info.NumberGroupSeparator
            }
            : new NumberFormatInfo
            {
                NumberDecimalDigits = fractionalDigitsCount,
                NumberDecimalSeparator = info.NumberDecimalSeparator,
                NumberGroupSeparator = info.NumberGroupSeparator
        };
    }
}

输出:

var d = new[] { 500000m, 500000.9m, 500000.99m, 500000.999m, 500000.9999m };
var info = new NumberFormatInfo { NumberDecimalSeparator = ",", NumberGroupSeparator = "" };

d.ToList().ForEach(x =>
{
    Console.WriteLine(String.Format(info.GetCustomFormatter(x), "{0:N}", x));
});

它从现有的500000 500000,90 500000,99 500000,999 500000,9999 抓取我们关心的属性,并返回一个包含我们想要的NumberFormatInfo的属性。它在丑陋的规模上相当高,但使用很简单。

答案 1 :(得分:1)

这是一个简洁而简单的解决方案(.NET Fiddle):

test[ column_numbers ]

如果您的值可能超过四位小数,则根据需要添加其他public static string FormatDecimal(decimal d) { string s = d.ToString("0.00##", NumberFormatInfo.InvariantInfo).Replace(".", ","); if (s.EndsWith(",00", StringComparison.Ordinal)) s = s.Substring(0, s.Length - 2); // chop off the "00" after integral values return s; } 个字符。格式字符串#包含28个小数位,可以容纳所有可能的0.00##########################值。

答案 2 :(得分:0)

不知道你希望这是多么优雅,但这是实现你所要求的直接方式。

List<decimal> decimals = new List<decimal>
{
    500000M,
    500000.9M,
    500000.99M,
    500000.999M,
    500000.9999M,
    500000.9000M 
};

foreach (decimal d in decimals)
{
    string dStr = d.ToString();
    if (!dStr.Contains("."))
    {
        Console.WriteLine(d + ",");
    }
    else
    {
        // Trim any trailing zeroes after the decimal point
        dStr = dStr.TrimEnd('0');

        string[] pieces = dStr.Split('.');
        if (pieces[1].Length < 2)
        {
            // Ensure 2 significant digits
            pieces[1] = pieces[1].PadRight(2, '0');
        }

        Console.WriteLine(String.Join(",", pieces));
    }
}

结果:

500000,
500000,90
500000,99
500000,999
500000,9999
500000,90