首页 文章

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

提问于
浏览
3

在给定以下规则的情况下,将十进制值转换为字符串的优雅方法是什么?

  • 显示小数点前的所有数字 .

  • 不变地显示逗号代替小数点 .

  • 如果小数点后的部分非零,则仅显示有效数字,但最少为2 .

例子:

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 回答

  • 1

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

    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;
    }
    

    如果您的值可能包含四位以上的小数位数,则根据需要添加其他 # 个字符 . 格式字符串 0.00########################## (包含28个小数位)将容纳所有可能的 decimal 值 .

  • 0

    我不会称之为漂亮,但它属于“它有效”的范畴 .

    首先是实施,

    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 抓取我们关心的属性,并返回一个带有我们想要的 NumberDecimalDigits 的新属性 . 它在丑陋的规模上相当高,但使用很简单 .

  • 2

    不知道你想要多么优雅,但这是一种直接的方式来实现你所要求的 .

    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
    

相关问题