Reputation: 704
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
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
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
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