ax1mx2
ax1mx2

Reputation: 704

.NET General Number Formatting with Thousands Separator

Is it possible in .NET to have the most compact number formatting like "G"eneral Number formatting, with extra thousands separator.

I can't use the following

String.Format("{0:#,###.###}",32445.324777M)

Because I get "32,445.325", and instead the result I want should be "32.445,325777". Is should also work with an arbitrary number of significant digits in the fractional part.

PS: I only need this for decimals.

Upvotes: 0

Views: 3435

Answers (3)

LeonSweden
LeonSweden

Reputation: 66

If you want full control create yoru own formatter lik below. See case "U" for your format.

public class CustomerFormatter : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        else
            return null;
    }

    public string Format(string format,
                          object arg,
                          IFormatProvider formatProvider)
    {
        if (!this.Equals(formatProvider))
        {
            return null;
        }
        else
        {
            // generic formatter if no formater specified
            if (String.IsNullOrEmpty(format))
                format = "G";

            // not a decimal type object
            if (!(arg is decimal))
                return null;

            // get value
            decimal val = (decimal)arg;

            // convert value into generic culture string for control of format
            string valueString = val.ToString();

            // get string in required format type
            format = format.ToUpper();
            switch (format)
            {
                // our user format
                case "U":

                    // get decimals
                    string decimals = val.ToString("G", CultureInfo.InvariantCulture);
                    decimals = decimals.Substring(decimals.IndexOf('.') + 1);

                    // get current culture info
                    NumberFormatInfo nfi = new CultureInfo(CultureInfo.CurrentCulture.Name).NumberFormat;

                    // set our separators
                    nfi.NumberGroupSeparator = ",";
                    nfi.NumberDecimalSeparator = ".";

                    // set numebr of decimals
                    nfi.NumberDecimalDigits = decimals.Length;

                    // convert value to our format
                    valueString = val.ToString("N", nfi);
                    break;
                default:
                    break;
            }

            return valueString;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        decimal dec = 32445.324777M;
        Console.WriteLine(String.Format(new CustomerFormatter(), "{0}", dec));
        Console.WriteLine(String.Format(new CustomerFormatter(), "{0:G}", dec));
        Console.WriteLine(String.Format(new CustomerFormatter(), "{0:U}", dec));
        Console.WriteLine(String.Format(new CustomerFormatter(), "{0:T}", dec));

        Console.ReadLine();
    }
}

Upvotes: 1

Luaan
Luaan

Reputation: 63772

That's where formatting culture comes in. You need to get a format specifier that matches your requirements. The default you have is usually the current culture, UI culture or invariant culture. The results you're getting imply you're using the US culture.

If you have a specific culture you want to output the number in, use that. If not, you can create your own:

var nfi = 
  new NumberFormatInfo 
  { 
    NumberDecimalSeparator = ",", 
    NumberGroupSeparator = "." 
  };

var ci = 
  new CultureInfo(CultureInfo.InvariantCulture.LCID) { NumberFormat = nfi };

return string.Format(ci, "{0:#,###.########}", 32445.324777M)

If you want to also get the most compact number, you'll have to use your own code. The easiest way would be to try both, and return the smaller resulting string.

If you want to, you can still use the string.Format syntax too - you can code your own ICustomFormatter to handle that:

void Main()
{
    var number = 32445.324777M;

    string.Format(new MyNumberFormatter(),  "{0:MyG}", number).Dump();
}

class MyNumberFormatter : IFormatProvider, ICustomFormatter
{
  public object GetFormat(Type type)
  {
    return this;
  }

  public string Format(string fmt, object arg, IFormatProvider formatProvider)
  {
    if (fmt != "MyG" || !(arg is decimal)) return string.Format(CultureInfo.CurrentCulture, "{0:" + fmt + "}", arg);

    return "Hi";
  }
}

This implementation is somewhat hacky, of course, I'm sure you can find better examples. But it does work. In the Format method, you can choose the format that fits better for the given number, or even just try something like doing the usual ToString("G", CultureInfo.InvariantCulture) and adding the decimal separators to that string. Whatever floats your boat :)

Upvotes: 5

Staeff
Staeff

Reputation: 5084

From the .NET documentation

The "#" custom format specifier serves as a digit-placeholder symbol. If the value that is being formatted has a digit in the position where the "#" symbol appears in the format string, that digit is copied to the result string.

Otherwise, nothing is stored in that position in the result string. Note that this specifier never displays a zero that is not a significant digit, even if zero is the only digit in the string. It will display zero only if it is a significant digit in the number that is being displayed.

The "##" format string causes the value to be rounded to the nearest digit preceding the decimal, where rounding away from zero is always used. For example, formatting 34.5 with "##" would result in the value 35.

It's not possible to format an unspecified amount of decimal places with the default formatting possibilities. So you should consider writing your own implementation if needed.

Also about the decimal and thousands separator, it depends upon your system settings, but you can override them by using a different culture as @Luaan described it in his answer.

You should also probably look into this answer.

Upvotes: 2

Related Questions