10

When I try the below code, I get the output 2 084 001. What could be wrong here? Isn't my format string supposed to override the current culture settings?

decimal v = 2084000.7621m;
System.Console.WriteLine(v.ToString("#,###,###"));
System.Console.ReadLine();

If I modify the code to use ToString("#,###,###", CultureInfo.InvariantCulture), I get the expected output, i.e. 2,084,001 but I cannot specify a format provider when I set the DataFormatString property on my data bound controls.

WARNING: When using an escaped literal group separator, as described below in the accepted and other answers, the literal character used is always output, even if it isn't needed, e.g. applying the format string #\,###\,### to a value of 324 results in an output value of ,,324.

ProfK
  • 49,207
  • 121
  • 399
  • 775
  • 2
    What locale is the default one? Looks like the default one has the `ThousandsSeparator` set to a space. – Oded Aug 26 '11 at 08:49
  • @Oded, it is en-ZA, and it does have that set to a space. That is why I am using an explicit format string, to try and override the default. – ProfK Aug 26 '11 at 09:08
  • 1
    I made the title more explicit for better recoverability, hope you agree. – Stefan Steinegger Aug 26 '11 at 09:50

2 Answers2

7

As far as I understand the Custom Numeric Format Strings, the , symbol is to place the group separator into the formatting (and not to specify which character to use). To make it an explicit symbol, you need to use the escape character \.

v.ToString(@"#\,###\,###")

Which has the disadvantage that the format string depends on the size of the number. In this case, you always get two commas in the resulting string, even if the number is smaller or higher.

To override the group separator, you need to specify your own NumberFormatInfo.

Got this test working:

decimal v = 2084000.7621m;

// clone the InvariantCulture to avoid specifying everything from scratch
var myNumberFormat = (NumberFormatInfo)CultureInfo.InvariantCulture.NumberFormat.Clone();
myNumberFormat.NumberGroupSeparator = "@";
myNumberFormat.NumberDecimalSeparator = "*";
Assert.AreEqual("2@084@000*7621", v.ToString("#,###,###.####", myNumberFormat));

It actually doesn't matter where and how many group separators you put into the format string. You could also make it: "#,#.####" with the exact same result. (Thats the advantage of using the group separator character, in contrast to the first solution).

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • 1
    Ya, thanks. If only the documentation was a little more explicit about this. I have actually had the same problem many years ago and totally was not reminded by anything in the docs. It also seems most forum answers elsewhere on the web don't consider this. – ProfK Aug 26 '11 at 09:36
1

Your problem is that the , character in your format string doesn't mean “insert a comma”; it actually means “insert the thousands separator for the current locale”, which happens to be a space in your default locale.

You will need to escape the comma by placing a backslash \ before it in your format string. You might need to use a double-backslash (or a verbatim string) to make the compiler accept the escape as you intended.

Stuart Cook
  • 3,994
  • 25
  • 23