How .NET reads a format string
A one-character format string is always a standard format: N is a number with the culture's group separators, and d plays the same role for a short date. Anything with more than one character is a custom format, parsed one specifier at a time, which is why a lone custom specifier needs a % prefix: without it, d means the whole short date pattern, and %d means just the day of the month.
Everything on this page is checked against .NET 8 itself, over tens of thousands of values, formats and cultures, and the culture data (month names, separators, currency patterns) is exported directly from .NET, not typed in by hand.
Numbers: what surprises people
- double stores binary, not decimal.
2.675.ToString("F2")is2.67because the stored value is slightly less than 2.675. The page shows the exact stored value. - Rounding differs by type. decimal rounds half away from zero (
0.125mwithF2is0.13); an exactly representable double rounds half to even (0.125is0.12). - decimal remembers its trailing zeros.
1.50m.ToString()is1.50;1.50.ToString()is1.5. - Negative zero exists for double.
(-0.001).ToString("F2")is-0.00since .NET Core 3.0. #shows nothing for zero.0.ToString("#.##")is an empty string; use0.##.- A comma at the end scales.
#,##0,,divides by a million: handy for "12M" style labels. - Invisible characters. Many cultures use a no-break space (U+00A0) or narrow no-break space (U+202F) as the group separator or before AM/PM. A test comparing against a normal space fails.
string.Format and interpolation: alignment and format
A placeholder is {index,alignment:format}, and interpolation uses the same parts: $"{price,10:N2}". A positive alignment pads on the left, a negative one on the right: $"[{"abc",6}]" is [ abc] and $"[{"abc",-6}]" is [abc ]. The format part is exactly what this page tests.
Leading zeros, hex and percent
D5pads an integer to five digits:42becomes00042, and-42becomes-00042.X4writes hex:255becomes00FF(x4for lower case).P1multiplies by 100:0.1234becomes12.3 %in the invariant culture, with a space that other cultures place differently.
FAQ
How do I round to 2 decimal places in C#?
For display, value.ToString("F2") (or N2 with group separators). For a rounded number, Math.Round(value, 2), which rounds exact halves to even unless you pass MidpointRounding.AwayFromZero. The two can disagree: Math.Round(0.125m, 2) is 0.12 but 0.125m.ToString("F2") is 0.13, and Math.Round(2.675, 2) is 2.68 but 2.675.ToString("F2") is 2.67.
Why does 2.675 formatted with F2 give 2.67?
A double cannot store 2.675 exactly. The stored value is 2.67499999999999982236431605997495353221893310546875, which rounds down. Use decimal for values like money: 2.675m.ToString("F2") is 2.68.
What is the difference between N2 and F2?
Both show two decimals. N2 adds group separators (1,234,567.89), F2 does not (1234567.89). Both use the culture's decimal separator.
Will I get the same output on my server?
Not unless you pass the culture explicitly. ToString and string interpolation otherwise fall back to the thread's current culture, which varies between machines and containers, and is the invariant culture under globalization-invariant mode. Because the culture data comes from ICU, results can also shift slightly between .NET and ICU versions.
How do I add leading zeros to a number?
42.ToString("D5") or 42.ToString("00000") gives 00042. D works on integers only; for text, "42".PadLeft(5, '0').
Why do I get "Input string was not in a correct format"?
That FormatException comes from parsing (int.Parse, decimal.Parse, Convert.ToInt32) or from a bad composite format such as string.Format("{0:N2"). See Input string was not in a correct format for every cause.