A decimal string with thousands of delimiters?

Consider the value of Decimal :

 Decimal value = -1234567890.1234789012M; 

I want to convert this Decimal value to a string and include "thousands separators".

Note. I don’t want to include thousands of separators , I want to include grouping of numbers . The difference is important for cultures that do not group numbers into thousands, or do not use commas to separate groups.

Example output with various standard formatting strings on my computer with my current locale:

 value.ToString() = -1234567890..1234789012 (Implicit General) value.ToString("g") = -1234567890..1234789012 (General) value.ToString("d") = FormatException (Decimal whole number) value.ToString("e") = -1..234568e++009 (Scientific) value.ToString("f") = -1234567890..123 (Fixed Point) value.ToString("n") = -12,,3456,,7890..123 (Number with commas for thousands) value.ToString("r") = FormatException (Round trippable) value.ToString("c") = -$$12,,3456,,7890..123 (Currency) value.ToString("#,0.#") = -12,,3456,,7890..1 

What I like (depending on the culture):

 en-US -1,234,567,890.1234789012 ca-ES -1.234.567.890,1234789012 gsw-FR -1 234 567 890,1234789012 (12/1/2012: fixed gws-FR to gsw-FR) fr-CH -1'234'567'890.1234789012 ar-DZ 1,234,567,890.1234789012- prs-AF 1.234.567.890,1234789012- ps-AF 1،234،567،890,1234789012- as-IN -1,23,45,67,890.1234789012 lo-LA (1234567,890.1234789012) (some debate if numbers should be "1,234,567,890") qps-PLOC 12,,3456,,7890..1234789012 

How to convert Decimal to a string with a grouping of numbers?


Refresh . Another desired result using my current culture:

 -1234567890M --> -12,,3456,,7890 -1234567890.1M --> -12,,3456,,7890..1 -1234567890.12M --> -12,,3456,,7890..12 -1234567890.123M --> -12,,3456,,7890..123 -1234567890.1234M --> -12,,3456,,7890..1234 -1234567890.12347M --> -12,,3456,,7890..12347 -1234567890.123478M --> -12,,3456,,7890..123478 -1234567890.1234789M --> -12,,3456,,7890..1234789 -1234567890.12347890M --> -12,,3456,,7890..1234789 -1234567890.123478901M --> -12,,3456,,7890..123478901 -1234567890.1234789012M --> -12,,3456,,7890..1234789012 

Update : I tried to understand how Decimal.ToString() manages to use the General format to show all the digits it needs to show:

 public override string ToString() { return Number.FormatDecimal(this, null, NumberFormatInfo.CurrentInfo); } 

except that Number.FormatDecimal is hiding somewhere:

 [MethodImpl(MethodImplOptions.InternalCall)] public static extern string FormatDecimal(decimal value, string format, NumberFormatInfo info); 

So it's a dead end.

+6
source share
3 answers

You can specify your own template (the template will accordingly allow the grouping method that corresponds to the culture, and the corresponding separator and decimal separator characters). A template can have positive, negative, and null sections . A positive template is always the same, but a negative template is culture dependent and can be extracted from the NumberaFormatInfo NumberNegativePattern property . Since you want as much precision as possible, you need to fill in 28-digit placeholders after the decimal; force grouping by comma.

  public static class DecimalFormatters { public static string ToStringNoTruncation(this Decimal n, IFormatProvider format) { NumberFormatInfo nfi = NumberFormatInfo.GetInstance(format); string[] numberNegativePatterns = { "(#,0.############################)", //0: (n) "-#,0.############################", //1: -n "- #,0.############################", //2: - n "#,0.############################-", //3: n- "#,0.############################ -"};//4: n - var pattern = "#,0.############################;" + numberNegativePatterns[nfi.NumberNegativePattern]; return n.ToString(pattern, format); } public static string ToStringNoTruncation(this Decimal n) { return n.ToStringNoTruncation(CultureInfo.CurrentCulture); } } 

Sample output

 Locale Output ======== ============================ en-US -1,234,567,890.1234789012 ca-ES -1.234.567.890,1234789012 hr-HR - 1.234.567.890,1234789012 gsw-FR -1 234 567 890,1234789012 fr-CH -1'234'567'890.1234789012 ar-DZ 1,234,567,890.1234789012- prs-AF 1.234.567.890,1234789012- ps-AF 1،234،567،890,1234789012- as-IN -1,23,45,67,890.1234789012 lo-LA (1234567,890.1234789012) qps-PLOC -12,,3456,,7890..1234789012 

There is currently no locale that uses NegativeNumberFormat 4 ( n - ), so this case cannot be tested. But there is no reason to think that this will fail.

+4
source

The Decimal ToString method uses CultureInfo.CurrentCulture for the user session by default and therefore varies depending on who the code is running.

The ToString method also accepts IFormatProvider in various overloads. Here you need to provide your formatters specific to your culture.

For example, if you pass NumberFormat to fr-CH, you can format everything that this culture expects:

 var culture = CultureInfo.CreateSpecificCulture("fr-CH"); Decimal value = -1234567890.1234789012M; Console.WriteLine(value.ToString("##,#.###############", culture.NumberFormat)); 

Will output

  -1'234'567'890.1234789012 

Change No. 3 - rewritten using custom formatters. This should do what you want based on a new updated question.

Edit # 4 - Took all your input and ran this:

 public void TestOutput() { PrintValue(-1234567890M); PrintValue(-1234567890.1M); PrintValue(-1234567890.12M); PrintValue(-1234567890.123M); PrintValue(-1234567890.1234M); PrintValue(-1234567890.12347M); PrintValue(-1234567890.123478M); PrintValue(-1234567890.1234789M); PrintValue(-1234567890.12347890M); PrintValue(-1234567890.123478901M); PrintValue(-1234567890.1234789012M); } private static void PrintValue(decimal value) { var culture = CultureInfo.CreateSpecificCulture("qps-PLOC"); Console.WriteLine(value.ToString("##,#.###############", culture.NumberFormat)); } 

Gives a result corresponding to what you set:

 --12,,3456,,7890 --12,,3456,,7890..1 --12,,3456,,7890..12 --12,,3456,,7890..123 --12,,3456,,7890..1234 --12,,3456,,7890..12347 --12,,3456,,7890..123478 --12,,3456,,7890..1234789 --12,,3456,,7890..1234789 --12,,3456,,7890..123478901 --12,,3456,,7890..1234789012 

As Joshua noted, this only works for some locations.

In appearance, then you need to choose the lesser of two evils: to know the accuracy of your numbers or to determine the formats for each culture. I would say that the accuracy of your numbers could be simpler. In this case, the previous version of my answer might be useful:

To explicitly control the number of decimal places for output, you can clone the number format provided by the culture and change the NumberDecimalDigits property.

 var culture = CultureInfo.CreateSpecificCulture("fr-CH"); Decimal value = -1234567890.1234789012M; NumberFormatInfo format = (NumberFormatInfo)culture.NumberFormat.Clone(); format.NumberDecimalDigits = 30; Console.WriteLine(value.ToString("n", format)); 

It is output:

 -1'234'567'890.123478901200000000000000000000 
+7
source

When formatting strings, you must enable culture. You can use String.Format and include the culture as the first parameter, or use the ToString object and use the overload that takes the culture.

The following code generates the expected result (except for gws-FR, it could not find the culture with this line).

 namespace CultureFormatting { using System; using System.Globalization; class Program { public static void Main() { Decimal value = -1234567890.1234789012M; Print("en-US", value); Print("ca-ES", value); //print("gws-FR", value); Print("fr-CH", value); Print("ar-DZ", value); Print("prs-AF", value); Print("ps-AF", value); Print("as-IN", value); Print("lo-LA", value); Print("qps-PLOC", value); } static void Print(string cultureName, Decimal value) { CultureInfo cultureInfo = new CultureInfo(cultureName); cultureInfo.NumberFormat.NumberDecimalDigits = 10; // Or, you could replace the {1:N} with {1:N10} to do the same // for just this string format call. string result = String.Format(cultureInfo, "{0,-8} {1:N}", cultureName, value); Console.WriteLine(result); } } } 

The above code outputs the following result:

 en-US -1,234,567,890.1234789012 ca-ES -1.234.567.890,1234789012 fr-CH -1'234'567'890.1234789012 ar-DZ 1,234,567,890.1234789012- prs-AF 1.234.567.890,1234789012- ps-AF 1،234،567،890,1234789012- as-IN -1,23,45,67,890.1234789012 lo-LA (1234567,890.1234789012) qps-PLOC --12,,3456,,7890..1234789012 

If you are working with a multi-threaded system such as ASP.Net, you can change the CurrentCulture property. Changing the thread culture will allow all associated ToString and String.Format use this culture.

Update

Since you want to display all the accuracy you will need to do a bit. Using NumberFormat.NumberDecimalDigits will work, except that if the value has less precision, the number will be output with trailing zeros. If you need to make sure that you display each digit without any additional functions, you need to calculate the accuracy in advance and set it before converting it to a string. The StackOverflow Calculate System.Decimal Precision and Scale question can help you determine the accuracy of the decimal.

+2
source

Source: https://habr.com/ru/post/920128/


All Articles