Double to string -- formatter missed? - c#

I have to convert a double to string with the following rules:
If decimal point position is -1 (or another non-existing value meaning 'Auto'), fractional part of the number should be output with all significant digits (all zeroes at the end should be trimmed). If the double is integer, its fractional part shouldn't output at all. For instance, digits = -1: 1029.0 -> 1,029, 1029.123456789 -> 1,029.123456789.
If decimal point position is equal or greater than 0, fractional part of the number should be output with the given number of digits. For instance, digits = 2: 1029.0 -> 1,029.00, 1029.123456789 -> 1,029.12.
Conversion should be culture-dependant (point or comma as decimal point, comma or space as group divider etc).
I have a code for the task:
var _Culture = CultureInfo.CreateSpecificCulture("en-US");
object sourceValue = 1029.0;//.123456789;
int digits = -1; // 2;
var formatter = "G";
if (digits != -1)
{
_Culture.NumberFormat.NumberDecimalDigits = digits;
formatter = "N";
}
var sourceValueAsFloat = (double)sourceValue;
var s = sourceValueAsFloat.ToString(formatter, _Culture);
Is there another formatter (not "N" or "G"), I can use instead? Or, maybe, I can use "N"/"G" another way?
Regards,

See here and here for all the format string specifiers .net understands.

// preferably make allDigits a static field to avoid re-allocating on every call
string allDigits = "#,0." + new string('#', 350);
string output = sourceValue.ToString(
digits < 0 ? allDigits : "#,0." + new string('0', digits));
And if you need to handle different cultures explicitly:
string output = sourceValue.ToString(
digits < 0 ? allDigits : "#,0." + new string('0', digits),
culture);

Related

Formatting decimal to string of length 9, 2 digits (0 padded) before '.' and 6 after it

I am a newbie in Biztalk and C#, I am trying to format the number in such a way that below requirement can be fulfilled
33.00 -> 33.000000
0.00 -> 00.000000
65.7777777 (random example that results in rounding) -> 65.7777777
So far I am successful with right padding. Below is the code
Param1 = "2.00"
if (param1.Contains("."))
{
Decimal convertDecimal = Convert.ToDecimal(param1);
String temp=convertDecimal.ToString("F6", System.Globalization.CultureInfo.InvariantCulture);
Console.WriteLine(temp);
}
Output : 2.000000
Expected Output : 02.000000
Any idea how can the leading zeros be included?
To add a leading '0' you can use padding or fix your format string to include a leading 0.
As for omitting rounding of 67.77777777777 to 67.7777778 you can format it to one digit more and use string slicing to remove the rounded digit:
using System;
var precision = 7;
// add a leading 0 and add precision + 1 digits after 0
var fmt = $"00.{new string('0', precision + 1)}";
foreach (var param1 in new[]{"0.0","22.00", "77.777777777777"})
{
Decimal convertDecimal = Convert.ToDecimal(param1);
String temp = convertDecimal.ToString(fmt);
Console.WriteLine(temp[..^1]); // temp = temp[..^1]; for later use
}
Output:
00.0000000
22.0000000
77.7777777 # temp is 77.77777778 - the 8 is sliced off the string
If you can not use slicing use string.substring(..) with lenght-1 instead.

Format decimal value with unusual rules

What's an elegant way to convert a decimal value to string given the following rules?
Display all digits before the decimal point.
Display a comma invariantly in place of the decimal point.
If the portion after the decimal point is nonzero, display only significant digits, but with a minimum of 2.
Examples:
decimal string
------------ ----------
500000 500000,
500000.9 500000,90
500000.90 500000,90
500000.900 500000,90
500000.9000 500000,90
500000.99 500000,99
500000.999 500000,999
500000.9999 500000,9999
I can easily display the portion before the decimal point, and the comma, by converting the value to int. But it's getting long and tedious handling the different cases for the portion after the decimal point.
If there were a way to specify that I wanted only the digits after the decimal point, but without the decimal point, I'd have this in hand. Something like String.Format("{0:.00#}", value), only don't display the decimal point.
I wouldn't call this pretty, but it falls under the category of "it works".
First the implementation,
public static class FormatProviderExtensions
{
public static IFormatProvider GetCustomFormatter(this NumberFormatInfo info, decimal d)
{
var truncated = Decimal.Truncate(d);
if (truncated == d)
{
return new NumberFormatInfo
{
NumberDecimalDigits = 0,
NumberDecimalSeparator = info.NumberDecimalSeparator,
NumberGroupSeparator = info.NumberGroupSeparator
};
}
// The 4th element contains the exponent of 10 used by decimal's
// representation - for more information see
// https://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
var fractionalDigitsCount = BitConverter.GetBytes(Decimal.GetBits(d)[3])[2];
return fractionalDigitsCount <= 2
? new NumberFormatInfo
{
NumberDecimalDigits = 2,
NumberDecimalSeparator = info.NumberDecimalSeparator,
NumberGroupSeparator = info.NumberGroupSeparator
}
: new NumberFormatInfo
{
NumberDecimalDigits = fractionalDigitsCount,
NumberDecimalSeparator = info.NumberDecimalSeparator,
NumberGroupSeparator = info.NumberGroupSeparator
};
}
}
and example usage:
var d = new[] { 500000m, 500000.9m, 500000.99m, 500000.999m, 500000.9999m };
var info = new NumberFormatInfo { NumberDecimalSeparator = ",", NumberGroupSeparator = "" };
d.ToList().ForEach(x =>
{
Console.WriteLine(String.Format(info.GetCustomFormatter(x), "{0:N}", x));
});
Outputs:
500000
500000,90
500000,99
500000,999
500000,9999
It grabs the properties we care about from an existing NumberFormatInfo and returns a new one with the NumberDecimalDigits we want. It's fairly high on the ugly scale, but the use is straightforward enough.
Here's a concise and simple solution (.NET Fiddle):
public static string FormatDecimal(decimal d)
{
string s = d.ToString("0.00##", NumberFormatInfo.InvariantInfo).Replace(".", ",");
if (s.EndsWith(",00", StringComparison.Ordinal))
s = s.Substring(0, s.Length - 2); // chop off the "00" after integral values
return s;
}
If your values might have more than four fractional digits, then add additional # characters as needed. The format string 0.00##########################, which has 28 fractional digits, will accommodate all possible decimal values.
Don't know how elegant you want this to be, but here's a straight-forward way of achieving what you're asking.
List<decimal> decimals = new List<decimal>
{
500000M,
500000.9M,
500000.99M,
500000.999M,
500000.9999M,
500000.9000M
};
foreach (decimal d in decimals)
{
string dStr = d.ToString();
if (!dStr.Contains("."))
{
Console.WriteLine(d + ",");
}
else
{
// Trim any trailing zeroes after the decimal point
dStr = dStr.TrimEnd('0');
string[] pieces = dStr.Split('.');
if (pieces[1].Length < 2)
{
// Ensure 2 significant digits
pieces[1] = pieces[1].PadRight(2, '0');
}
Console.WriteLine(String.Join(",", pieces));
}
}
Results:
500000,
500000,90
500000,99
500000,999
500000,9999
500000,90

Double to string with mandatory decimal point

This is probably dumb but it's giving me a hard time. I need to convert/format a double to string with a mandatory decimal point.
1 => 1.0
0.2423423 => 0.2423423
0.1 => 0.1
1234 => 1234.0
Basically, I want to output all decimals but also make sure the rounded values have the redundant .0 too. I am sure there is a simple way to achieve this.
Use double.ToString("N1"):
double d1 = 1d;
double d2 = 0.2423423d;
double d3 = 0.1d;
double d4 = 1234d;
Console.WriteLine(d1.ToString("N1"));
Console.WriteLine(d2.ToString("N1"));
Console.WriteLine(d3.ToString("N1"));
Console.WriteLine(d4.ToString("N1"));
Demo
Standard Numeric Format Strings
The Numeric ("N") Format Specifier
Update
(1.234).ToString("N1") produces 1.2 and in addition to removing additional decimal digits, it also adds a thousands separator
Well, perhaps you need to implement a custom NumberFormatInfo object which you can derive from the current CultureInfo and use in double.ToString:
var culture = CultureInfo.CurrentCulture;
var customNfi = (NumberFormatInfo)culture.NumberFormat.Clone();
customNfi.NumberDecimalDigits = 1;
customNfi.NumberGroupSeparator = "";
Console.WriteLine(d1.ToString(customNfi));
Note that you need to clone it since it's readonly by default.
Demo
There is not a built in method to append a mandatory .0 to the end of whole numbers with the .ToString() method, as the existing formats will truncate or round based on the number of decimal places you specify.
My suggestion is to just roll your own implementation with an extension method
public static String ToDecmialString(this double source)
{
if ((source % 1) == 0)
return source.ToString("f1");
else
return source.ToString();
}
And the usage:
double d1 = 1;
double d2 = 0.2423423;
double d3 = 0.1;
double d4 = 1234;
Console.WriteLine(d1.ToDecimalString());
Console.WriteLine(d2.ToDecimalString());
Console.WriteLine(d3.ToDecimalString());
Console.WriteLine(d4.ToDecimalString());
Results in this output:
1.0
0.2423423
0.1
1234.0
You could do something like this: if the number doesn't have decimal points you can format its output to enforce one decimal 0 and if it has decimal places, just use ToString();
double a1 = 1;
double a2 = 0.2423423;
string result = string.Empty;
if(a1 - Math.Floor(a1) >0.0)
result = a1.ToString();
else
result = a1.ToString("F1");
if (a2 - Math.Floor(a2) > 0.0)
result = a2.ToString();
else
result = a2.ToString("F1");
When you use "F" as formatting, the output won't contain thousands separator and the number that follows it specifies the number of decimal places.
Use ToString("0.0###########################").
It does work. I found it in duplicate of your question decimal ToString formatting which gives at least 1 digit, no upper limit
Double provides a method ToString() where you can pass an IFormatProvider-object stating how you want your double to be converted.
Additionally, it should display trailing 0 at all costs.
value = 16034.125E21;
// Display value using the invariant culture.
Console.WriteLine(value.ToString(CultureInfo.InvariantCulture));
// Display value using the en-GB culture.
Console.WriteLine(value.ToString(CultureInfo.CreateSpecificCulture("en-GB")));
// Display value using the de-DE culture.
Console.WriteLine(value.ToString(CultureInfo.CreateSpecificCulture("de-DE")));
// This example displays the following output to the console:
// -16325.62015
// -16325.62015
// -16325,62015
// 1.6034125E+25
// 1.6034125E+25
// 1,6034125E+25
Here is the documentation from MSDN.
You can cast to string and then appen ".0" if there was no decimal point given
string sValue=doubleValue.ToString();
if(!sValue.Contains('.'))
sValue+=".0";
EDIT:
As mentioned in the comments '.' may not be the decimal seperator in the current culture. Refer to this article to retrieve the actual seperator if you want make your code save for this case.

Convert integer number to three digit

I have an integer variable .if its 1-9 it only displays as "1" or "9", I'm looking to convert the variable to save as 3 digits, ie. "001", or "009", etc. any ideas?
I am using C#,ASP.Net
use
int X = 9;
string PaddedResult = X.ToString().PadLeft (3, '0'); // results in 009
see MSDN references here and here.
What about
var result = String.Format("{0:000}", X);
var result2 = X.ToString("000");
int i = 5;
string tVal=i.ToString("000");
From: https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#DFormatString
The "D" (or decimal) format specifier converts a number to a string of decimal digits (0-9), prefixed by a minus sign if the number is negative.
The precision specifier indicates the minimum number of digits desired in the resulting string. If required, the number is padded with zeros to its left to produce the number of digits given by the precision specifier.
Like:
int value;
value = 12345;
Console.WriteLine(value.ToString("D"));
// Displays 12345
Console.WriteLine(value.ToString("D8"));
// Displays 00012345
value = -12345;
Console.WriteLine(value.ToString("D"));
// Displays -12345
Console.WriteLine(value.ToString("D8"));
// Displays -00012345
int i = 5;
string retVal = i.ToString().PadLeft(3, '0');
you can use this code also
int k = 5;
string name = $"name-{k++:D3}.ext";

Best way to display decimal without trailing zeroes

Is there a display formatter that will output decimals as these string representations in c# without doing any rounding?
// decimal -> string
20 -> 20
20.00 -> 20
20.5 -> 20.5
20.5000 -> 20.5
20.125 -> 20.125
20.12500 -> 20.125
0.000 -> 0
{0.#} will round, and using some Trim type function will not work with a bound numeric column in a grid.
Do you have a maximum number of decimal places you'll ever need to display? (Your examples have a max of 5).
If so, I would think that formatting with "0.#####" would do what you want.
static void Main(string[] args)
{
var dList = new decimal[] { 20, 20.00m, 20.5m, 20.5000m, 20.125m, 20.12500m, 0.000m };
foreach (var d in dList)
Console.WriteLine(d.ToString("0.#####"));
}
I just learned how to properly use the G format specifier. See the MSDN Documentation. There is a note a little way down that states that trailing zeros will be preserved for decimal types when no precision is specified. Why they would do this I do not know, but specifying the maximum number of digits for our precision should fix that problem. So for formatting decimals, G29 is the best bet.
decimal test = 20.5000m;
test.ToString("G"); // outputs 20.5000 like the documentation says it should
test.ToString("G29"); // outputs 20.5 which is exactly what we want
This string format should make your day: "0.#############################". Keep in mind that decimals can have at most 29 significant digits though.
Examples:
? (1000000.00000000000050000000000m).ToString("0.#############################")
-> 1000000.0000000000005
? (1000000.00000000000050000000001m).ToString("0.#############################")
-> 1000000.0000000000005
? (1000000.0000000000005000000001m).ToString("0.#############################")
-> 1000000.0000000000005000000001
? (9223372036854775807.0000000001m).ToString("0.#############################")
-> 9223372036854775807
? (9223372036854775807.000000001m).ToString("0.#############################")
-> 9223372036854775807.000000001
This is yet another variation of what I saw above. In my case I need to preserve all significant digits to the right of the decimal point, meaning drop all zeros after the most significant digit. Just thought it would be nice to share. I cannot vouch for the efficiency of this though, but when try to achieve aesthetics, you are already pretty much damned to inefficiencies.
public static string ToTrimmedString(this decimal target)
{
string strValue = target.ToString(); //Get the stock string
//If there is a decimal point present
if (strValue.Contains("."))
{
//Remove all trailing zeros
strValue = strValue.TrimEnd('0');
//If all we are left with is a decimal point
if (strValue.EndsWith(".")) //then remove it
strValue = strValue.TrimEnd('.');
}
return strValue;
}
That's all, just wanted to throw in my two cents.
Another solution, based on dyslexicanaboko's answer, but independent of the current culture:
public static string ToTrimmedString(this decimal num)
{
string str = num.ToString();
string decimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
if (str.Contains(decimalSeparator))
{
str = str.TrimEnd('0');
if(str.EndsWith(decimalSeparator))
{
str = str.RemoveFromEnd(1);
}
}
return str;
}
public static string RemoveFromEnd(this string str, int characterCount)
{
return str.Remove(str.Length - characterCount, characterCount);
}
You can use the G0 format string if you are happy to accept scientific notation as per the documentation:
Fixed-point notation is used if the exponent that would result from expressing the number in scientific notation is greater than -5 and less than the precision specifier; otherwise, scientific notation is used.
You can use this format string as a parameter to the .ToString() method, or by specifying it as a within an interpolated string. Both are shown below.
decimal hasTrailingZeros = 20.12500m;
Console.WriteLine(hasTrailingZeros.ToString("G0")); // outputs 20.125
Console.WriteLine($"{hasTrailingZeros:G0}"); // outputs 20.125
decimal fourDecimalPlaces = 0.0001m;
Console.WriteLine(fourDecimalPlaces.ToString("G0")); // outputs 0.0001
Console.WriteLine($"{fourDecimalPlaces:G0}"); // outputs 0.0001
decimal fiveDecimalPlaces = 0.00001m;
Console.WriteLine(fiveDecimalPlaces.ToString("G0")); // outputs 1E-05
Console.WriteLine($"{fiveDecimalPlaces:G0}"); // outputs 1E-05
Extension method:
public static class Extensions
{
public static string TrimDouble(this string temp)
{
var value = temp.IndexOf('.') == -1 ? temp : temp.TrimEnd('.', '0');
return value == string.Empty ? "0" : value;
}
}
Example code:
double[] dvalues = {20, 20.00, 20.5, 20.5000, 20.125, 20.125000, 0.000};
foreach (var value in dvalues)
Console.WriteLine(string.Format("{0} --> {1}", value, value.ToString().TrimDouble()));
Console.WriteLine("==================");
string[] svalues = {"20", "20.00", "20.5", "20.5000", "20.125", "20.125000", "0.000"};
foreach (var value in svalues)
Console.WriteLine(string.Format("{0} --> {1}", value, value.TrimDouble()));
Output:
20 --> 20
20 --> 20
20,5 --> 20,5
20,5 --> 20,5
20,125 --> 20,125
20,125 --> 20,125
0 --> 0
==================
20 --> 20
20.00 --> 2
20.5 --> 20.5
20.5000 --> 20.5
20.125 --> 20.125
20.125000 --> 20.125
0.000 --> 0
I don't think it's possible out-of-the-box but a simple method like this should do it
public static string TrimDecimal(decimal value)
{
string result = value.ToString(System.Globalization.CultureInfo.InvariantCulture);
if (result.IndexOf('.') == -1)
return result;
return result.TrimEnd('0', '.');
}
It's quite easy to do out of the box:
Decimal YourValue; //just as example
String YourString = YourValue.ToString().TrimEnd('0','.');
that will remove all trailing zeros from your Decimal.
The only thing that you need to do is add .ToString().TrimEnd('0','.'); to a decimal variable to convert a Decimal into a String without trailing zeros, like in the example above.
In some regions it should be a .ToString().TrimEnd('0',','); (where they use a comma instead of a point, but you can also add a dot and a comma as parameters to be sure).
(you can also add both as parameters)
decimal val = 0.000000000100m;
string result = val == 0 ? "0" : val.ToString().TrimEnd('0').TrimEnd('.');
I ended up with the following code:
public static string DropTrailingZeros(string test)
{
if (test.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
{
test = test.TrimEnd('0');
}
if (test.EndsWith(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
{
test = test.Substring(0,
test.Length - CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator.Length);
}
return test;
}
I have end up with this variant:
public static string Decimal2StringCompact(decimal value, int maxDigits)
{
if (maxDigits < 0) maxDigits = 0;
else if (maxDigits > 28) maxDigits = 28;
return Math.Round(value, maxDigits, MidpointRounding.ToEven).ToString("0.############################", CultureInfo.InvariantCulture);
}
Advantages:
you can specify the max number of significant digits after the point to display at runtime;
you can explicitly specify a round method;
you can explicitly control a culture.
You can create extension method
public static class ExtensionMethod {
public static decimal simplify_decimal(this decimal value) => decimal.Parse($"{this:0.############}");
}

Categories