Format decimal value with unusual rules

What's an elegant way to convert a decimal value to a string, given the following rules?

  • Display all digits up to the decimal point.
  • Display comma invariantly instead of decimal point.
  • If the part after the decimal point is nonzero, display only significant digits, but with a minimum number of characters.

Examples:

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

      

I can easily display the part before the decimal point and comma by converting the value to int

. But it gets long and tedious, handling different cases for the part after the decimal point.

If there was a way to indicate that I only want the digits after the decimal point, but no decimal point, I would have it in my hand. Something like String.Format("{0:.00#}", value)

, just doesn't display the decimal point.

+3


source to share


3 answers


Here's a short and simple solution ( .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;
}

      



If your values ​​can contain more than four fractional digits, add additional characters #

as needed. A format string 0.00##########################

that has 28 decimal digits will contain all possible values decimal

.

+1


source


I wouldn't call it cute, but it falls under the "it works" category.

Implementation first,

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

      

and an example of use:



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

      

Outputs:

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

      

It grabs the properties we want from the existing NumberFormatInfo

one and returns the new one with the NumberDecimalDigits

one we want. It's pretty high on an ugly scale, but the usage is simple enough.

+2


source


I don't know how elegant you are, but here is a direct way to achieve what you ask.

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

      

Results:

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

      

0


source







All Articles